<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the startup action.
//!
//! The startup action collects basic information about the system (e.g. its
//! boot time) and metadata about the agent (e.g. it's name and version). It is
//! special in a sense that generally it should be not invoked by flows, but be
//! called explicitly during agent startup.

use std::time::{Duration, SystemTime, UNIX_EPOCH};

use log::error;

use crate::session::{Session, Error, Result};

/// A response type for the startup action.
pub struct Response {
    /// Time of last system boot.
    boot_time: SystemTime,
    /// Name of the GRR agent.
    name: String,
    /// Description of the GRR agent.
    description: String,
    /// Version of the GRR agent.
    version: Version,
}

/// Handles requests for the startup action.
pub fn handle<S: Session>(session: &mut S, _: ()) -> Result<()> {
    let boot_time = boot_time().map_err(Error::action)?;

    session.send(Response {
        boot_time: boot_time,
        name: String::from(env!("CARGO_PKG_NAME")),
        description: String::from(env!("CARGO_PKG_DESCRIPTION")),
        version: Version::from_crate(),
    })?;

    Ok(())
}

/// Returns information about the system boot time.
fn boot_time() -> std::result::Result<SystemTime, sys_info::Error> {
    let timeval = sys_info::boottime()?;

    let secs = timeval.tv_sec as u64;
    let micros = timeval.tv_usec as u64;

    Ok(UNIX_EPOCH + Duration::from_secs(secs) + Duration::from_micros(micros))
}

/// A type for representing version metadata.
struct Version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub revision: u8,
}

impl Version {

    /// Constructs version metadata from Crate information.
    ///
    /// This function assumes that are relevant crate information is correctly
    /// specified in the `Cargo.toml` file.
    fn from_crate() -> Version {
        Version {
            major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0),
            minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0),
            patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0),
            revision: env!("CARGO_PKG_VERSION_PRE").parse().unwrap_or(0),
        }
    }

    /// Returns a numeric representation of version metadata.
    ///
    /// This function assumes that all version components are smaller than 10.
    /// In other cases, the output is undefined (but the function call itself
    /// does not panic).
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg::action::startup::Version;
    ///
    /// let version = Version {
    ///     major: 1,
    ///     minor: 2,
    ///     patch: 3,
    ///     revision: 4,
    /// };
    ///
    /// assert_eq!(version.as_numeric(), 1234)
    /// ```
    fn as_numeric(&self) -> u32 {
        let mut result = 0;
        result = 10 * result + self.major as u32;
        result = 10 * result + self.minor as u32;
        result = 10 * result + self.patch as u32;
        result = 10 * result + self.revision as u32;
        result
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StartupInfo");

    type Proto = rrg_proto::StartupInfo;

    fn into_proto(self) -> rrg_proto::StartupInfo {
        let boot_time_micros = match rrg_proto::micros(self.boot_time) {
            Ok(boot_time_micros) => boot_time_micros,
            Err(error) => {
                error!("failed to convert boot time: {}", error);
                0
            }
        };

        rrg_proto::StartupInfo {
            client_info: Some(rrg_proto::ClientInformation {
                client_name: Some(self.name),
                client_version: Some(self.version.as_numeric()),
                client_description: Some(self.description),
                ..Default::default()
            }),
            boot_time: Some(boot_time_micros),
        }
    }
}
<commit_msg>Refactor error handling in the startup action.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the startup action.
//!
//! The startup action collects basic information about the system (e.g. its
//! boot time) and metadata about the agent (e.g. it's name and version). It is
//! special in a sense that generally it should be not invoked by flows, but be
//! called explicitly during agent startup.

use std::fmt::{Display, Formatter};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use log::error;

use crate::session::{self, Session};

#[derive(Debug)]
struct Error {
    boot_time_error: sys_info::Error,
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&self.boot_time_error)
    }
}

impl Display for Error {

    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "failed to obtain boot time: {}", self.boot_time_error)
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// A response type for the startup action.
pub struct Response {
    /// Time of last system boot.
    boot_time: SystemTime,
    /// Name of the GRR agent.
    name: String,
    /// Description of the GRR agent.
    description: String,
    /// Version of the GRR agent.
    version: Version,
}

/// Handles requests for the startup action.
pub fn handle<S: Session>(session: &mut S, _: ()) -> session::Result<()> {
    let boot_time = boot_time()?;

    session.send(Response {
        boot_time: boot_time,
        name: String::from(env!("CARGO_PKG_NAME")),
        description: String::from(env!("CARGO_PKG_DESCRIPTION")),
        version: Version::from_crate(),
    })?;

    Ok(())
}

/// Returns information about the system boot time.
fn boot_time() -> std::result::Result<SystemTime, Error> {
    // TODO: Consider not failing on failures to obtain the boot time. This is
    // really not that critical and sending the rest of the client metadata is
    // more important.
    let timeval = match sys_info::boottime() {
        Ok(timeval) => timeval,
        Err(error) => return Err(Error { boot_time_error: error }),
    };

    let secs = timeval.tv_sec as u64;
    let micros = timeval.tv_usec as u64;

    Ok(UNIX_EPOCH + Duration::from_secs(secs) + Duration::from_micros(micros))
}

/// A type for representing version metadata.
struct Version {
    pub major: u8,
    pub minor: u8,
    pub patch: u8,
    pub revision: u8,
}

impl Version {

    /// Constructs version metadata from Crate information.
    ///
    /// This function assumes that are relevant crate information is correctly
    /// specified in the `Cargo.toml` file.
    fn from_crate() -> Version {
        Version {
            major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0),
            minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0),
            patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0),
            revision: env!("CARGO_PKG_VERSION_PRE").parse().unwrap_or(0),
        }
    }

    /// Returns a numeric representation of version metadata.
    ///
    /// This function assumes that all version components are smaller than 10.
    /// In other cases, the output is undefined (but the function call itself
    /// does not panic).
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg::action::startup::Version;
    ///
    /// let version = Version {
    ///     major: 1,
    ///     minor: 2,
    ///     patch: 3,
    ///     revision: 4,
    /// };
    ///
    /// assert_eq!(version.as_numeric(), 1234)
    /// ```
    fn as_numeric(&self) -> u32 {
        let mut result = 0;
        result = 10 * result + self.major as u32;
        result = 10 * result + self.minor as u32;
        result = 10 * result + self.patch as u32;
        result = 10 * result + self.revision as u32;
        result
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StartupInfo");

    type Proto = rrg_proto::StartupInfo;

    fn into_proto(self) -> rrg_proto::StartupInfo {
        let boot_time_micros = match rrg_proto::micros(self.boot_time) {
            Ok(boot_time_micros) => boot_time_micros,
            Err(error) => {
                error!("failed to convert boot time: {}", error);
                0
            }
        };

        rrg_proto::StartupInfo {
            client_info: Some(rrg_proto::ClientInformation {
                client_name: Some(self.name),
                client_version: Some(self.version.as_numeric()),
                client_description: Some(self.description),
                ..Default::default()
            }),
            boot_time: Some(boot_time_micros),
        }
    }
}
<|endoftext|>


<commit_before>// mrusty. mruby bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::mem;
use std::rc::Rc;

use super::mruby_ffi::*;

/// A safe `struct` for the mruby API.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "2 + 2 == 5").unwrap();
///
/// assert_eq!(result.to_bool().unwrap(), false);
/// ```
pub struct MRuby {
    mrb: *mut MRState,
    ctx: *mut MRContext,
    classes: Box<HashMap<String, (*mut MRClass, MRDataType)>>,
    methods: Box<HashMap<String, Box<HashMap<u32, Box<Fn(&Rc<RefCell<MRuby>>, Value) -> Value>>>>>
}

impl MRuby {
    /// Creates an mruby state and context stored in a `struct`.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// let mruby = MRuby::new();
    /// ```
    pub fn new() -> Rc<RefCell<MRuby>> {
        unsafe {
            let mrb = mrb_open();

            let mruby = Rc::new(RefCell::new(
                MRuby {
                    mrb: mrb,
                    ctx: mrbc_context_new(mrb),
                    classes: Box::new(HashMap::new()),
                    methods: Box::new(HashMap::new())
                }
            ));

            mrb_ext_set_ud(mrb, mem::transmute::<&Rc<RefCell<MRuby>>, *const u8>(&mruby));

            mruby
        }
    }

    fn close(&self) {
        unsafe {
            mrb_close(self.mrb);
        }
    }
}

/// Runs mruby `script` on a state and context and returns a `Value` in an `Ok`
/// or an `Err` containing an mruby exception.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "true").unwrap();
///
/// assert_eq!(result.to_bool().unwrap(), true);
/// ```
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "'' + 1");
///
/// assert_eq!(result, Err("TypeError: expected String"));
/// ```
pub fn run<'a>(mruby: &'a Rc<RefCell<MRuby>>, script: &str) -> Result<Value, &'a str> {
    unsafe {
        let value = mrb_load_string_cxt(mruby.borrow().mrb, CString::new(script).unwrap().as_ptr(), mruby.borrow().ctx);
        let exc = mrb_ext_get_exc(mruby.borrow().mrb);

        match exc.typ {
            MRType::MRB_TT_STRING => Err(exc.to_str(mruby.borrow().mrb).unwrap()),
            _                     => Ok(Value::new(mruby.clone(), value))
        }
    }
}

/// Defines Rust type `T` as an mruby `Class` named `name`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::def_class;
/// let mut mruby = MRuby::new();
///
/// struct Cont {
///     value: i32
/// }
///
/// def_class::<Cont>(&mruby, "Container");
/// ```
pub fn def_class<T>(mruby: &Rc<RefCell<MRuby>>, name: &str) {
    unsafe {
        let name = name.to_string();

        let c_name = CString::new(name.clone()).unwrap();
        let object = CString::new("Object").unwrap();
        let object = mrb_class_get(mruby.borrow().mrb, object.as_ptr());

        let class = mrb_define_class(mruby.borrow().mrb, c_name.as_ptr(), object);

        mrb_ext_set_instance_tt(class, MRType::MRB_TT_DATA);

        extern "C" fn free<T>(_mrb: *mut MRState, ptr: *const u8) {
            unsafe {
                Box::from_raw(ptr as *mut T);
            }
        }

        let data_type = MRDataType { name: c_name.as_ptr(), free: free::<T> };

        mruby.borrow_mut().classes.insert(name.clone(), (class, data_type));
        mruby.borrow_mut().methods.insert(name, Box::new(HashMap::new()));
    }
}

/// Defines Rust type `T` as an mruby `Class` named `name`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// # use mrusty::def_class;
/// # use mrusty::def_method;
/// # use mrusty::fixnum;
/// let mut mruby = MRuby::new();
///
/// struct Cont;
///
/// def_class::<Cont>(&mruby, "Container");
/// def_method(&mruby, "Container", "hi", |mruby, _slf| fixnum(&mruby, 2));
///
/// let result = run(&mruby, "Container.new.hi").unwrap();
///
/// assert_eq!(result.to_i32().unwrap(), 2);
/// ```
pub fn def_method<F>(mruby: &Rc<RefCell<MRuby>>, class: &str, name: &str, method: F)
    where F: Fn(&Rc<RefCell<MRuby>>, Value) -> Value + 'static {
    {
        let sym = unsafe {
            mrb_intern_cstr(mruby.borrow().mrb, CString::new(name.clone()).unwrap().as_ptr())
        };

        let mut borrow = mruby.borrow_mut();

        let methods = match borrow.methods.get_mut(class) {
            Some(methods) => methods,
            None       => panic!("{} class not found.", class)
        };

        methods.insert(sym, Box::new(method));
    }

    extern "C" fn call_method(mrb: *mut MRState, slf: MRValue) -> MRValue {
        unsafe {
            let ptr = mrb_ext_get_ud(mrb);
            let mruby = mem::transmute::<*const u8, &Rc<RefCell<MRuby>>>(ptr);

            let value = Value::new(mruby.clone(), slf);

            let class = mrb_funcall(mrb, slf, CString::new("class").unwrap().as_ptr(), 0);
            let class = mrb_funcall(mrb, class, CString::new("to_s").unwrap().as_ptr(), 0);
            let class = class.to_str(mrb).unwrap();

            let borrow = mruby.borrow();

            let methods = match borrow.methods.get(class) {
                Some(methods) => methods,
                None          => panic!("{} class not found.", class)
            };

            let sym = mrb_ext_get_mid(mrb);

            let method = match methods.get(&sym) {
                Some(method) => method,
                None         => panic!("Method not found.")
            };

            method(mruby, value).value
        }
    }

    let borrow = mruby.borrow();

    let class = match borrow.classes.get(class) {
        Some(class) => class,
        None       => panic!("{} class not found.", class)
    };

    unsafe {
        mrb_define_method(mruby.borrow().mrb, class.0, CString::new(name).unwrap().as_ptr(), call_method, 1 << 12);
    }
}

/// Creates mruby `Value` of `Class` `Fixnum`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::fixnum;
/// let mut mruby = MRuby::new();
///
/// let fixn = fixnum(&mruby, 2);
///
/// assert_eq!(fixn.to_i32().unwrap(), 2);
/// ```
pub fn fixnum(mruby: &Rc<RefCell<MRuby>>, value: i32) -> Value {
    unsafe {
        Value::new(mruby.clone(), MRValue::fixnum(value))
    }
}

/// Creates mruby `Value` of `Class` `name` containing a Rust object of type `T`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::def_class;
/// # use mrusty::obj;
/// let mut mruby = MRuby::new();
///
/// struct Cont {
///     value: i32
/// }
///
/// def_class::<Cont>(&mruby, "Container");
///
/// let value = obj(&mruby, Cont { value: 3 }, "Container");
/// ```
pub fn obj<T>(mruby: &Rc<RefCell<MRuby>>, obj: T, name: &str) -> Value {
    let borrow = mruby.borrow();

    let class = match borrow.classes.get(name) {
        Some(class) => class,
        None       => panic!("{} class not found.", name)
    };

    let boxed = Box::into_raw(Box::new(obj));

    unsafe {
        Value::new(mruby.clone(), MRValue::obj(mruby.borrow().mrb, class.0 as *mut MRClass, boxed, &class.1))
    }
}

impl Drop for MRuby {
    fn drop(&mut self) {
        self.close();
    }
}

#[derive(Clone)]
pub struct Value {
    mruby: Rc<RefCell<MRuby>>,
    value: MRValue
}

impl Value {
    fn new(mruby: Rc<RefCell<MRuby>>, value: MRValue) -> Value {
        Value {
            mruby: mruby,
            value: value
        }
    }

    /// Casts a `Value` and returns a `bool` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   def pos(n)
    ///     n > 0
    ///   end
    ///
    ///   pos 1
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_bool().unwrap(), true);
    /// ```
    pub fn to_bool(&self) -> Result<bool, &str> {
        unsafe {
            self.value.to_bool()
        }
    }

    /// Casts a `Value` and returns an `i32` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   def fact(n)
    ///     n > 1 ? fact(n - 1) * n : 1
    ///   end
    ///
    ///   fact 5
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_i32().unwrap(), 120);
    /// ```
    pub fn to_i32(&self) -> Result<i32, &str> {
        unsafe {
            self.value.to_i32()
        }
    }

    /// Casts a `Value` and returns an `f64` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   3 / 2.0
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_f64().unwrap(), 1.5);
    /// ```
    pub fn to_f64(&self) -> Result<f64, &str> {
        unsafe {
            self.value.to_f64()
        }
    }

    /// Casts a `Value` and returns a `&str` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   [1, 2, 3].map(&:to_s).join
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_str().unwrap(), "123");
    /// ```
    pub fn to_str<'b>(&self) -> Result<&'b str, &str> {
        unsafe {
            self.value.to_str(self.mruby.borrow().mrb)
        }
    }

    /// Casts mruby `Value` of `Class` `name` to Rust type `&T`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::def_class;
    /// # use mrusty::obj;
    /// let mut mruby = MRuby::new();
    ///
    /// struct Cont {
    ///     value: i32
    /// }
    ///
    /// def_class::<Cont>(&mruby, "Container");
    ///
    /// let value = obj(&mruby, Cont { value: 3 }, "Container");
    /// let cont: &Cont = value.to_obj("Container").unwrap();
    ///
    /// assert_eq!(cont.value, 3);
    /// ```
    pub fn to_obj<T>(&self, name: &str) -> Result<&T, &str> {
        unsafe {
            let borrow = self.mruby.borrow();

            let class = match borrow.classes.get(name) {
                Some(class) => class,
                None       => panic!("{} class not found.", name)
            };

            self.value.to_obj::<T>(self.mruby.borrow().mrb, &class.1)
        }
    }
}

use std::fmt;

impl PartialEq<Value> for Value {
    fn eq(&self, other: &Value) -> bool {
        self.value.eq(&other.value)
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Value {{ {:?} }}", self.value)
    }
}
<commit_msg>Fixed corrupt pointer and removed unnecessary muts.<commit_after>// mrusty. mruby bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::mem;
use std::rc::Rc;

use super::mruby_ffi::*;

/// A safe `struct` for the mruby API.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "2 + 2 == 5").unwrap();
///
/// assert_eq!(result.to_bool().unwrap(), false);
/// ```
pub struct MRuby {
    mrb: *mut MRState,
    ctx: *mut MRContext,
    classes: Box<HashMap<String, (*mut MRClass, MRDataType)>>,
    methods: Box<HashMap<String, Box<HashMap<u32, Box<Fn(&Rc<RefCell<MRuby>>, Value) -> Value>>>>>
}

impl MRuby {
    /// Creates an mruby state and context stored in a `struct`.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// let mruby = MRuby::new();
    /// ```
    pub fn new() -> Rc<RefCell<MRuby>> {
        unsafe {
            let mrb = mrb_open();

            let mruby = Rc::new(RefCell::new(
                MRuby {
                    mrb: mrb,
                    ctx: mrbc_context_new(mrb),
                    classes: Box::new(HashMap::new()),
                    methods: Box::new(HashMap::new())
                }
            ));

            mrb_ext_set_ud(mrb, mem::transmute::<Rc<RefCell<MRuby>>, *const u8>(mruby.clone()));

            mruby
        }
    }

    fn close(&self) {
        unsafe {
            mrb_close(self.mrb);
        }
    }
}

/// Runs mruby `script` on a state and context and returns a `Value` in an `Ok`
/// or an `Err` containing an mruby exception.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "true").unwrap();
///
/// assert_eq!(result.to_bool().unwrap(), true);
/// ```
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// let mruby = MRuby::new();
/// let result = run(&mruby, "'' + 1");
///
/// assert_eq!(result, Err("TypeError: expected String"));
/// ```
pub fn run<'a>(mruby: &'a Rc<RefCell<MRuby>>, script: &str) -> Result<Value, &'a str> {
    unsafe {
        let value = mrb_load_string_cxt(mruby.borrow().mrb, CString::new(script).unwrap().as_ptr(), mruby.borrow().ctx);
        let exc = mrb_ext_get_exc(mruby.borrow().mrb);

        match exc.typ {
            MRType::MRB_TT_STRING => Err(exc.to_str(mruby.borrow().mrb).unwrap()),
            _                     => Ok(Value::new(mruby.clone(), value))
        }
    }
}

/// Defines Rust type `T` as an mruby `Class` named `name`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::def_class;
/// let mruby = MRuby::new();
///
/// struct Cont {
///     value: i32
/// }
///
/// def_class::<Cont>(&mruby, "Container");
/// ```
pub fn def_class<T>(mruby: &Rc<RefCell<MRuby>>, name: &str) {
    unsafe {
        let name = name.to_string();

        let c_name = CString::new(name.clone()).unwrap();
        let object = CString::new("Object").unwrap();
        let object = mrb_class_get(mruby.borrow().mrb, object.as_ptr());

        let class = mrb_define_class(mruby.borrow().mrb, c_name.as_ptr(), object);

        mrb_ext_set_instance_tt(class, MRType::MRB_TT_DATA);

        extern "C" fn free<T>(_mrb: *mut MRState, ptr: *const u8) {
            unsafe {
                Box::from_raw(ptr as *mut T);
            }
        }

        let data_type = MRDataType { name: c_name.as_ptr(), free: free::<T> };

        mruby.borrow_mut().classes.insert(name.clone(), (class, data_type));
        mruby.borrow_mut().methods.insert(name, Box::new(HashMap::new()));
    }
}

/// Defines Rust type `T` as an mruby `Class` named `name`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::run;
/// # use mrusty::def_class;
/// # use mrusty::def_method;
/// # use mrusty::fixnum;
/// let mruby = MRuby::new();
///
/// struct Cont;
///
/// def_class::<Cont>(&mruby, "Container");
/// def_method(&mruby, "Container", "hi", |mruby, _slf| fixnum(&mruby, 2));
///
/// let result = run(&mruby, "Container.new.hi").unwrap();
///
/// assert_eq!(result.to_i32().unwrap(), 2);
/// ```
pub fn def_method<F>(mruby: &Rc<RefCell<MRuby>>, class: &str, name: &str, method: F)
    where F: Fn(&Rc<RefCell<MRuby>>, Value) -> Value + 'static {
    {
        let sym = unsafe {
            mrb_intern_cstr(mruby.borrow().mrb, CString::new(name.clone()).unwrap().as_ptr())
        };

        let mut borrow = mruby.borrow_mut();

        let methods = match borrow.methods.get_mut(class) {
            Some(methods) => methods,
            None       => panic!("{} class not found.", class)
        };

        methods.insert(sym, Box::new(method));
    }

    extern "C" fn call_method(mrb: *mut MRState, slf: MRValue) -> MRValue {
        unsafe {
            let ptr = mrb_ext_get_ud(mrb);
            let mruby = mem::transmute::<*const u8, Rc<RefCell<MRuby>>>(ptr);

            let value = Value::new(mruby.clone(), slf);

            let class = mrb_funcall(mrb, slf, CString::new("class").unwrap().as_ptr(), 0);
            let class = mrb_funcall(mrb, class, CString::new("to_s").unwrap().as_ptr(), 0);
            let class = class.to_str(mrb).unwrap();

            let borrow = mruby.borrow();

            let methods = match borrow.methods.get(class) {
                Some(methods) => methods,
                None          => panic!("{} class not found.", class)
            };

            let sym = mrb_ext_get_mid(mrb);

            let method = match methods.get(&sym) {
                Some(method) => method,
                None         => panic!("Method not found.")
            };

            method(&mruby, value).value
        }
    }

    let borrow = mruby.borrow();

    let class = match borrow.classes.get(class) {
        Some(class) => class,
        None       => panic!("{} class not found.", class)
    };

    unsafe {
        mrb_define_method(mruby.borrow().mrb, class.0, CString::new(name).unwrap().as_ptr(), call_method, 1 << 12);
    }
}

/// Creates mruby `Value` of `Class` `Fixnum`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::fixnum;
/// let mruby = MRuby::new();
///
/// let fixn = fixnum(&mruby, 2);
///
/// assert_eq!(fixn.to_i32().unwrap(), 2);
/// ```
pub fn fixnum(mruby: &Rc<RefCell<MRuby>>, value: i32) -> Value {
    unsafe {
        Value::new(mruby.clone(), MRValue::fixnum(value))
    }
}

/// Creates mruby `Value` of `Class` `name` containing a Rust object of type `T`.
///
/// # Examples
///
/// ```
/// # use mrusty::MRuby;
/// # use mrusty::def_class;
/// # use mrusty::obj;
/// let mruby = MRuby::new();
///
/// struct Cont {
///     value: i32
/// }
///
/// def_class::<Cont>(&mruby, "Container");
///
/// let value = obj(&mruby, Cont { value: 3 }, "Container");
/// ```
pub fn obj<T>(mruby: &Rc<RefCell<MRuby>>, obj: T, name: &str) -> Value {
    let borrow = mruby.borrow();

    let class = match borrow.classes.get(name) {
        Some(class) => class,
        None       => panic!("{} class not found.", name)
    };

    let boxed = Box::into_raw(Box::new(obj));

    unsafe {
        Value::new(mruby.clone(), MRValue::obj(mruby.borrow().mrb, class.0 as *mut MRClass, boxed, &class.1))
    }
}

impl Drop for MRuby {
    fn drop(&mut self) {
        self.close();
    }
}

#[derive(Clone)]
pub struct Value {
    mruby: Rc<RefCell<MRuby>>,
    value: MRValue
}

impl Value {
    fn new(mruby: Rc<RefCell<MRuby>>, value: MRValue) -> Value {
        Value {
            mruby: mruby,
            value: value
        }
    }

    /// Casts a `Value` and returns a `bool` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   def pos(n)
    ///     n > 0
    ///   end
    ///
    ///   pos 1
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_bool().unwrap(), true);
    /// ```
    pub fn to_bool(&self) -> Result<bool, &str> {
        unsafe {
            self.value.to_bool()
        }
    }

    /// Casts a `Value` and returns an `i32` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   def fact(n)
    ///     n > 1 ? fact(n - 1) * n : 1
    ///   end
    ///
    ///   fact 5
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_i32().unwrap(), 120);
    /// ```
    pub fn to_i32(&self) -> Result<i32, &str> {
        unsafe {
            self.value.to_i32()
        }
    }

    /// Casts a `Value` and returns an `f64` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   3 / 2.0
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_f64().unwrap(), 1.5);
    /// ```
    pub fn to_f64(&self) -> Result<f64, &str> {
        unsafe {
            self.value.to_f64()
        }
    }

    /// Casts a `Value` and returns a `&str` in an `Ok` or an `Err` if the types mismatch.
    ///
    /// # Example
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::run;
    /// let mruby = MRuby::new();
    /// let result = run(&mruby, "
    ///   [1, 2, 3].map(&:to_s).join
    /// ").unwrap();
    ///
    /// assert_eq!(result.to_str().unwrap(), "123");
    /// ```
    pub fn to_str<'b>(&self) -> Result<&'b str, &str> {
        unsafe {
            self.value.to_str(self.mruby.borrow().mrb)
        }
    }

    /// Casts mruby `Value` of `Class` `name` to Rust type `&T`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::def_class;
    /// # use mrusty::obj;
    /// let mruby = MRuby::new();
    ///
    /// struct Cont {
    ///     value: i32
    /// }
    ///
    /// def_class::<Cont>(&mruby, "Container");
    ///
    /// let value = obj(&mruby, Cont { value: 3 }, "Container");
    /// let cont: &Cont = value.to_obj("Container").unwrap();
    ///
    /// assert_eq!(cont.value, 3);
    /// ```
    pub fn to_obj<T>(&self, name: &str) -> Result<&T, &str> {
        unsafe {
            let borrow = self.mruby.borrow();

            let class = match borrow.classes.get(name) {
                Some(class) => class,
                None       => panic!("{} class not found.", name)
            };

            self.value.to_obj::<T>(self.mruby.borrow().mrb, &class.1)
        }
    }
}

use std::fmt;

impl PartialEq<Value> for Value {
    fn eq(&self, other: &Value) -> bool {
        self.value.eq(&other.value)
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Value {{ {:?} }}", self.value)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::fmt;
use std::marker::PhantomData;
use raw_mutex::RawMutex;

/// A mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// # Differences from the standard library `Mutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `Mutex` due to platform limitations.
/// - A `MutexGuard` can be sent to another thread and unlocked there.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
/// use parking_lot::Mutex;
/// use std::thread;
/// use std::sync::mpsc::channel;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(Mutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (data.clone(), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub struct Mutex<T: ?Sized> {
    raw: RawMutex,
    data: UnsafeCell<T>,
}

unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
#[must_use]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
    mutex: &'a Mutex<T>,
    marker: PhantomData<&'a mut T>,
}

impl<T> Mutex<T> {
    /// Creates a new mutex in an unlocked state ready for use.
    #[cfg(feature = "nightly")]
    #[inline]
    pub const fn new(val: T) -> Mutex<T> {
        Mutex {
            data: UnsafeCell::new(val),
            raw: RawMutex::new(),
        }
    }

    /// Creates a new mutex in an unlocked state ready for use.
    #[cfg(not(feature = "nightly"))]
    #[inline]
    pub fn new(val: T) -> Mutex<T> {
        Mutex {
            data: UnsafeCell::new(val),
            raw: RawMutex::new(),
        }
    }

    /// Consumes this mutex, returning the underlying data.
    #[inline]
    pub fn into_inner(self) -> T {
        unsafe { self.data.into_inner() }
    }
}

impl<T: ?Sized> Mutex<T> {
    /// Acquires a mutex, blocking the current thread until it is able to do so.
    ///
    /// This function will block the local thread until it is available to acquire
    /// the mutex. Upon returning, the thread is the only thread with the mutex
    /// held. An RAII guard is returned to allow scoped unlock of the lock. When
    /// the guard goes out of scope, the mutex will be unlocked.
    ///
    /// Attempts to lock a mutex in the thread which already holds the lock will
    /// result is a deadlock.
    #[inline]
    pub fn lock(&self) -> MutexGuard<T> {
        self.raw.lock();
        MutexGuard {
            mutex: self,
            marker: PhantomData,
        }
    }

    /// Attempts to acquire this lock.
    ///
    /// If the lock could not be acquired at this time, then `Err` is returned.
    /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
    /// guard is dropped.
    ///
    /// This function does not block.
    #[inline]
    pub fn try_lock(&self) -> Option<MutexGuard<T>> {
        if self.raw.try_lock() {
            Some(MutexGuard {
                mutex: self,
                marker: PhantomData,
            })
        } else {
            None
        }
    }

    /// Returns a mutable reference to the underlying data.
    ///
    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
    /// take place---the mutable borrow statically guarantees no locks exist.
    #[inline]
    pub fn get_mut(&mut self) -> &mut T {
        unsafe { &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Mutex<T> {
    #[inline]
    fn default() -> Mutex<T> {
        Mutex::new(Default::default())
    }
}

impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.try_lock() {
            Some(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard),
            None => write!(f, "Mutex {{ <locked> }}"),
        }
    }
}

impl<'a, T: ?Sized + 'a> Deref for MutexGuard<'a, T> {
    type Target = T;
    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.mutex.data.get() }
    }
}

impl<'a, T: ?Sized + 'a> DerefMut for MutexGuard<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.mutex.data.get() }
    }
}

impl<'a, T: ?Sized + 'a> Drop for MutexGuard<'a, T> {
    #[inline]
    fn drop(&mut self) {
        self.mutex.raw.unlock();
    }
}

// Helper function used by Condvar, not publicly exported
#[inline]
pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a RawMutex {
    &guard.mutex.raw
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::thread;
    use {Mutex, Condvar};

    struct Packet<T>(Arc<(Mutex<T>, Condvar)>);

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    unsafe impl<T: Send> Send for Packet<T> {}
    unsafe impl<T> Sync for Packet<T> {}

    #[test]
    fn smoke() {
        let m = Mutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        lazy_static! {
            static ref M: Mutex<()> = Mutex::new(());
        }
        static mut CNT: u32 = 0;
        const J: u32 = 1000;
        const K: u32 = 3;

        fn inc() {
            for _ in 0..J {
                unsafe {
                    let _g = M.lock();
                    CNT += 1;
                }
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(unsafe { CNT }, J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = Mutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = Mutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = Mutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = Mutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_condvar() {
        let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
        let packet2 = Packet(packet.0.clone());
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            // wait until parent gets in
            rx.recv().unwrap();
            let &(ref lock, ref cvar) = &*packet2.0;
            let mut lock = lock.lock();
            *lock = true;
            cvar.notify_one();
        });

        let &(ref lock, ref cvar) = &*packet.0;
        let mut lock = lock.lock();
        tx.send(()).unwrap();
        assert!(!*lock);
        while !*lock {
            cvar.wait(&mut lock);
        }
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(Mutex::new(1));
        let arc2 = Arc::new(Mutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(Mutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || -> () {
                struct Unwinder {
                    i: Arc<Mutex<i32>>,
                }
                impl Drop for Unwinder {
                    fn drop(&mut self) {
                        *self.i.lock() += 1;
                    }
                }
                let _u = Unwinder { i: arc2 };
                panic!();
            })
            .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_send() {
        fn send<T: Send>(_: T) {}

        let mutex = Mutex::new(());
        send(mutex.lock());
    }
}
<commit_msg>Fix doc on Mutex<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::fmt;
use std::marker::PhantomData;
use raw_mutex::RawMutex;

/// A mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// # Differences from the standard library `Mutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `Mutex` due to platform limitations.
/// - A `MutexGuard` can be sent to another thread and unlocked there.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
/// use parking_lot::Mutex;
/// use std::thread;
/// use std::sync::mpsc::channel;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(Mutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (data.clone(), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub struct Mutex<T: ?Sized> {
    raw: RawMutex,
    data: UnsafeCell<T>,
}

unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
#[must_use]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
    mutex: &'a Mutex<T>,
    marker: PhantomData<&'a mut T>,
}

impl<T> Mutex<T> {
    /// Creates a new mutex in an unlocked state ready for use.
    #[cfg(feature = "nightly")]
    #[inline]
    pub const fn new(val: T) -> Mutex<T> {
        Mutex {
            data: UnsafeCell::new(val),
            raw: RawMutex::new(),
        }
    }

    /// Creates a new mutex in an unlocked state ready for use.
    #[cfg(not(feature = "nightly"))]
    #[inline]
    pub fn new(val: T) -> Mutex<T> {
        Mutex {
            data: UnsafeCell::new(val),
            raw: RawMutex::new(),
        }
    }

    /// Consumes this mutex, returning the underlying data.
    #[inline]
    pub fn into_inner(self) -> T {
        unsafe { self.data.into_inner() }
    }
}

impl<T: ?Sized> Mutex<T> {
    /// Acquires a mutex, blocking the current thread until it is able to do so.
    ///
    /// This function will block the local thread until it is available to acquire
    /// the mutex. Upon returning, the thread is the only thread with the mutex
    /// held. An RAII guard is returned to allow scoped unlock of the lock. When
    /// the guard goes out of scope, the mutex will be unlocked.
    ///
    /// Attempts to lock a mutex in the thread which already holds the lock will
    /// result is a deadlock.
    #[inline]
    pub fn lock(&self) -> MutexGuard<T> {
        self.raw.lock();
        MutexGuard {
            mutex: self,
            marker: PhantomData,
        }
    }

    /// Attempts to acquire this lock.
    ///
    /// If the lock could not be acquired at this time, then `None` is returned.
    /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
    /// guard is dropped.
    ///
    /// This function does not block.
    #[inline]
    pub fn try_lock(&self) -> Option<MutexGuard<T>> {
        if self.raw.try_lock() {
            Some(MutexGuard {
                mutex: self,
                marker: PhantomData,
            })
        } else {
            None
        }
    }

    /// Returns a mutable reference to the underlying data.
    ///
    /// Since this call borrows the `Mutex` mutably, no actual locking needs to
    /// take place---the mutable borrow statically guarantees no locks exist.
    #[inline]
    pub fn get_mut(&mut self) -> &mut T {
        unsafe { &mut *self.data.get() }
    }
}

impl<T: ?Sized + Default> Default for Mutex<T> {
    #[inline]
    fn default() -> Mutex<T> {
        Mutex::new(Default::default())
    }
}

impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.try_lock() {
            Some(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard),
            None => write!(f, "Mutex {{ <locked> }}"),
        }
    }
}

impl<'a, T: ?Sized + 'a> Deref for MutexGuard<'a, T> {
    type Target = T;
    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.mutex.data.get() }
    }
}

impl<'a, T: ?Sized + 'a> DerefMut for MutexGuard<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.mutex.data.get() }
    }
}

impl<'a, T: ?Sized + 'a> Drop for MutexGuard<'a, T> {
    #[inline]
    fn drop(&mut self) {
        self.mutex.raw.unlock();
    }
}

// Helper function used by Condvar, not publicly exported
#[inline]
pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a RawMutex {
    &guard.mutex.raw
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::thread;
    use {Mutex, Condvar};

    struct Packet<T>(Arc<(Mutex<T>, Condvar)>);

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    unsafe impl<T: Send> Send for Packet<T> {}
    unsafe impl<T> Sync for Packet<T> {}

    #[test]
    fn smoke() {
        let m = Mutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        lazy_static! {
            static ref M: Mutex<()> = Mutex::new(());
        }
        static mut CNT: u32 = 0;
        const J: u32 = 1000;
        const K: u32 = 3;

        fn inc() {
            for _ in 0..J {
                unsafe {
                    let _g = M.lock();
                    CNT += 1;
                }
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            thread::spawn(move || {
                inc();
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(unsafe { CNT }, J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = Mutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = Mutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = Mutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = Mutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_condvar() {
        let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
        let packet2 = Packet(packet.0.clone());
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            // wait until parent gets in
            rx.recv().unwrap();
            let &(ref lock, ref cvar) = &*packet2.0;
            let mut lock = lock.lock();
            *lock = true;
            cvar.notify_one();
        });

        let &(ref lock, ref cvar) = &*packet.0;
        let mut lock = lock.lock();
        tx.send(()).unwrap();
        assert!(!*lock);
        while !*lock {
            cvar.wait(&mut lock);
        }
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(Mutex::new(1));
        let arc2 = Arc::new(Mutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(Mutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || -> () {
                struct Unwinder {
                    i: Arc<Mutex<i32>>,
                }
                impl Drop for Unwinder {
                    fn drop(&mut self) {
                        *self.i.lock() += 1;
                    }
                }
                let _u = Unwinder { i: arc2 };
                panic!();
            })
            .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_send() {
        fn send<T: Send>(_: T) {}

        let mutex = Mutex::new(());
        send(mutex.lock());
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(unused_variables)]

use racc::runtime::{ParserTables, ParserState};

use syntax::parse::token;
use syntax::codemap::{spanned, mk_sp, BytePos};
use syntax::ast;
use syntax::ptr::P;
use std;

#[deriving(Show)]
struct Context {
    x: ()
}

#[deriving(Show)]
enum Value {
    RawToken(token::Token),
    ASTItem(P<ast::Item>),
    ASTBlock(P<ast::Block>),
    ASTStmt(P<ast::Stmt>),
    ASTPat(P<ast::Pat>),
    ASTExpr(P<ast::Expr>),
    ASTTy(P<ast::Ty>)
}

pub type Parser = ParserState<Context, Option<Value>>;
const Z : BytePos = BytePos(0);

grammar! {

    Context cx;
    Option<Value>;

    LIT;
    EQEQ;

    Expr : LIT=lit {
        match lit {
            Some(Value::RawToken(token::Literal(token::Integer(name), None))) => {
                let n = std::num::from_str_radix(name.as_str(), 10).unwrap();
                let elit =
                    P(spanned(Z, Z,
                              ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus))));
                Some(Value::ASTExpr(P(ast::Expr { id: 0,
                                                  node: ast::ExprLit(elit),
                                                  span: mk_sp(Z, Z) })))
            }
            _ => panic!("unexpected lit value")
        }
    }
    | Expr=a EQEQ Expr=b {
        match (a, b) {
            (Some(Value::ASTExpr(aa)), Some(Value::ASTExpr(bb))) => {
                Some(Value::ASTExpr(P(ast::Expr { id: 0,
                                                  node: ast::ExprBinary(ast::BiEq, aa, bb),
                                                  span: mk_sp(Z, Z) })))
            }
            _ => panic!("unexpected binop '==' values")
        }
    }
    ;
}

#[test]
fn test_parse()
{
    use syntax::parse::token::intern;
    use racc::runtime::FinishParseResult;

    let toks = vec![
        (LIT, token::Literal(token::Integer(intern("7654")), None)),
        (EQEQ, token::EqEq),
        (LIT, token::Literal(token::Integer(intern("1234")), None))
    ];

    let mut parser = ParserState::new(get_parser_tables());
    let mut cx = Context { x: () };

    for &(tok, ref val) in toks.iter() {
        parser.push_token(&mut cx, tok, Some(Value::RawToken(val.clone())));
    }

    match parser.finish(&mut cx) {
        FinishParseResult::Accepted(final_value) => {
            println!("Accepted: {}", final_value);
        }
        FinishParseResult::SyntaxError => {
            panic!("SyntaxError");
        }
    }
}
<commit_msg>Bump for travis.<commit_after>#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(unused_variables)]

use racc::runtime::{ParserTables, ParserState};

use syntax::ast;
use syntax::ptr::P;
use syntax::parse::token;
use syntax::codemap::{spanned, mk_sp, BytePos};
use std;

#[deriving(Show)]
struct Context {
    x: ()
}

#[deriving(Show)]
enum Value {
    RawToken(token::Token),
    ASTItem(P<ast::Item>),
    ASTBlock(P<ast::Block>),
    ASTStmt(P<ast::Stmt>),
    ASTPat(P<ast::Pat>),
    ASTExpr(P<ast::Expr>),
    ASTTy(P<ast::Ty>)
}

pub type Parser = ParserState<Context, Option<Value>>;
const Z : BytePos = BytePos(0);

grammar! {

    Context cx;
    Option<Value>;

    LIT;
    EQEQ;

    Expr : LIT=lit {
        match lit {
            Some(Value::RawToken(token::Literal(token::Integer(name), None))) => {
                let n = std::num::from_str_radix(name.as_str(), 10).unwrap();
                let elit =
                    P(spanned(Z, Z,
                              ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus))));
                Some(Value::ASTExpr(P(ast::Expr { id: 0,
                                                  node: ast::ExprLit(elit),
                                                  span: mk_sp(Z, Z) })))
            }
            _ => panic!("unexpected lit value")
        }
    }
    | Expr=a EQEQ Expr=b {
        match (a, b) {
            (Some(Value::ASTExpr(aa)), Some(Value::ASTExpr(bb))) => {
                Some(Value::ASTExpr(P(ast::Expr { id: 0,
                                                  node: ast::ExprBinary(ast::BiEq, aa, bb),
                                                  span: mk_sp(Z, Z) })))
            }
            _ => panic!("unexpected binop '==' values")
        }
    }
    ;
}

#[test]
fn test_parse()
{
    use syntax::parse::token::intern;
    use racc::runtime::FinishParseResult;

    let toks = vec![
        (LIT, token::Literal(token::Integer(intern("7654")), None)),
        (EQEQ, token::EqEq),
        (LIT, token::Literal(token::Integer(intern("1234")), None))
    ];

    let mut parser = ParserState::new(get_parser_tables());
    let mut cx = Context { x: () };

    for &(tok, ref val) in toks.iter() {
        parser.push_token(&mut cx, tok, Some(Value::RawToken(val.clone())));
    }

    match parser.finish(&mut cx) {
        FinishParseResult::Accepted(final_value) => {
            println!("Accepted: {}", final_value);
        }
        FinishParseResult::SyntaxError => {
            panic!("SyntaxError");
        }
    }
}
<|endoftext|>


<commit_before>//! Parser and syntax tree.

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, u32, Option<u32>, Greedy),
    Capture(~Expr)
}

/// Greediness flag.
#[deriving(Eq)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

///
/// The maximum number of repetitions.  Any number larger than this will
/// cause a syntax error.  By honoring this limit, we prevent integer
/// overflow bugs in the library.
///
/// The value (100000) is taken from Ruby.
///
static REPEAT_MAX: u32 = 100000;

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

///
/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    wrap_in(Concatenate, items)
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];
    let mut had_repeat = false;

    loop {
        let mut is_repeat = false;
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '[' => items.push(cc_to_expr(p_charclass(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(inner, min, max, greedy) => {
                            if had_repeat && greedy == Greedy {
                                Repeat(inner, min, max, NonGreedy)
                            } else {
                                fail!("multiple repeat")
                            }
                        },
                        _ => {
                            is_repeat = true;
                            Repeat(~e, 0, Some(1), Greedy)
                        }
                    });
                },
                '+' => {
                    add_repeat(&mut items, 1, None);
                    is_repeat = true;
                },
                '*' => {
                    add_repeat(&mut items, 0, None);
                    is_repeat = true;
                },
                '{' => {
                    let (min, max) = p_repetition(s);
                    add_repeat(&mut items, min, max);
                    is_repeat = true;
                },
                _ => items.push(Range(c, c))
            },
            None => break
        };
        had_repeat = is_repeat;
    }

    wrap_in(Concatenate, items)
}

#[inline]
fn wrap_in(wrapper: fn(~[Expr]) -> Expr, mut items: ~[Expr]) -> Expr {
    match items.len() {
        0 => Empty,
        1 => items.pop().unwrap(),
        _ => wrapper(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => (),
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: u32, max: Option<u32>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

///
/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
fn p_repetition(s: &mut State) -> (u32, Option<u32>) {
    let min = p_number(s);
    match s.advance() {
        Some(',') => {
            let max = p_number(s);
            match s.advance() {
                // {} or {M,} or {,N} or {M,N}
                Some('}') => {
                    let min_ = min.unwrap_or(0);
                    if check_repeat(min_, max) {
                        (min_, max)
                    } else {
                        fail!("bad repeat interval")
                    }
                },
                _ => fail!("invalid repeat")
            }
        },
        Some('}') => match min {
            // {N}
            Some(n) => (n, Some(n)),
            _ => fail!("invalid repeat")
        },
        _ => fail!("invalid repeat")
    }
}

#[inline]
fn check_repeat(min: u32, max: Option<u32>) -> bool {
    match max {
        Some(max_) => min <= max_,
        None => true
    }
}

///
/// Parse a non-negative integer, and return it as a `u32`.
///
/// This returns `None` if no number could be parsed, but fails directly
/// if the number is greater than `REPEAT_MAX`.
///
fn p_number(s: &mut State) -> Option<u32> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as u32 - '0' as u32;
                acc = Some(match acc {
                    Some(n) => {
                        let acc_ = 10 * n + digit;
                        if acc_ <= REPEAT_MAX {
                            acc_
                        } else {
                            fail!(format!("repeat must be <= {}", REPEAT_MAX))
                        }
                    },
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'D' => ascii::digit.negate(),
            'S' => ascii::space.negate(),
            'W' => ascii::word.negate(),

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ if ascii::punct.includes(c) => CharClass::from_char(c),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Parse a character class (e.g. `[a-z]`), sans the opening bracket.
fn p_charclass(s: &mut State) -> CharClass {
    let mut classes: ~[CharClass] = ~[];

    let negate = match s.advance() {
        Some('^') => true,
        _ => { s.retreat(); false }
    };

    loop {
        match s.advance() {
            Some(c) => match c {
                ']' => break,
                '-' => match p_charclass_token(s) {
                    Some(cc_hi) => match classes.pop() {
                        Some(cc_lo) => {
                            // [a-z]
                            let lo = cc_lo.to_char().expect("bad character range");
                            let hi = cc_hi.to_char().expect("bad character range");
                            classes.push(CharClass::from_range(lo, hi));
                        },
                        None => classes.push(cc_hi)  // [-z]
                    },
                    None => classes.push(CharClass::from_char('-'))  // [a-]
                },
                _ => {
                    s.retreat();
                    classes.push(p_charclass_token(s).expect("invalid char class"));
                }
            },
            None => fail!("unexpected end of char class")
        }
    }

    let cc = CharClass::combine(classes);
    if negate {
        cc.negate()
    } else {
        cc
    }
}

fn p_charclass_token(s: &mut State) -> Option<CharClass> {
    match s.advance() {
        Some(c) => match c {
            ']' => { s.retreat(); None },
            '[' => Some(p_charclass(s)),
            '\\' => Some(p_escape(s)),
            _ => Some(CharClass::from_char(c))
        },
        None => None
    }
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}

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

    #[test]
    #[should_fail]
    fn issue_3() {
        let _ = parse("(?:a+)?");  // error: multiple repeat
    }
}
<commit_msg>Don't allow repeats on empty expressions (fixes #4)<commit_after>//! Parser and syntax tree.

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, u32, Option<u32>, Greedy),
    Capture(~Expr)
}

impl Expr {
    pub fn accepts_empty(&self) -> bool {
        match *self {
            Empty => true,
            Range(..) => false,
            Concatenate(ref inners) => inners.iter().all(|e| e.accepts_empty()),
            Alternate(ref inners) => inners.iter().any(|e| e.accepts_empty()),
            Repeat(ref inner, min, _, _) => min == 0 || inner.accepts_empty(),
            Capture(ref inner) => inner.accepts_empty()
        }
    }
}

/// Greediness flag.
#[deriving(Eq)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

///
/// The maximum number of repetitions.  Any number larger than this will
/// cause a syntax error.  By honoring this limit, we prevent integer
/// overflow bugs in the library.
///
/// The value (100000) is taken from Ruby.
///
static REPEAT_MAX: u32 = 100000;

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

///
/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    wrap_in(Concatenate, items)
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];
    let mut had_repeat = false;

    loop {
        let mut is_repeat = false;
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '[' => items.push(cc_to_expr(p_charclass(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(inner, min, max, greedy) => {
                            if had_repeat && greedy == Greedy {
                                Repeat(inner, min, max, NonGreedy)
                            } else {
                                fail!("multiple repeat")
                            }
                        },
                        _ => {
                            is_repeat = true;
                            Repeat(~e, 0, Some(1), Greedy)
                        }
                    });
                },
                '+' => {
                    add_repeat(&mut items, 1, None);
                    is_repeat = true;
                },
                '*' => {
                    add_repeat(&mut items, 0, None);
                    is_repeat = true;
                },
                '{' => {
                    let (min, max) = p_repetition(s);
                    add_repeat(&mut items, min, max);
                    is_repeat = true;
                },
                _ => items.push(Range(c, c))
            },
            None => break
        };
        had_repeat = is_repeat;
    }

    wrap_in(Concatenate, items)
}

#[inline]
fn wrap_in(wrapper: fn(~[Expr]) -> Expr, mut items: ~[Expr]) -> Expr {
    match items.len() {
        0 => Empty,
        1 => items.pop().unwrap(),
        _ => wrapper(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => (),
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: u32, max: Option<u32>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ if max.is_none() && e.accepts_empty() => fail!("cannot repeat the empty string"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

///
/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
fn p_repetition(s: &mut State) -> (u32, Option<u32>) {
    let min = p_number(s);
    match s.advance() {
        Some(',') => {
            let max = p_number(s);
            match s.advance() {
                // {} or {M,} or {,N} or {M,N}
                Some('}') => {
                    let min_ = min.unwrap_or(0);
                    if check_repeat(min_, max) {
                        (min_, max)
                    } else {
                        fail!("bad repeat interval")
                    }
                },
                _ => fail!("invalid repeat")
            }
        },
        Some('}') => match min {
            // {N}
            Some(n) => (n, Some(n)),
            _ => fail!("invalid repeat")
        },
        _ => fail!("invalid repeat")
    }
}

#[inline]
fn check_repeat(min: u32, max: Option<u32>) -> bool {
    match max {
        Some(max_) => min <= max_,
        None => true
    }
}

///
/// Parse a non-negative integer, and return it as a `u32`.
///
/// This returns `None` if no number could be parsed, but fails directly
/// if the number is greater than `REPEAT_MAX`.
///
fn p_number(s: &mut State) -> Option<u32> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as u32 - '0' as u32;
                acc = Some(match acc {
                    Some(n) => {
                        let acc_ = 10 * n + digit;
                        if acc_ <= REPEAT_MAX {
                            acc_
                        } else {
                            fail!(format!("repeat must be <= {}", REPEAT_MAX))
                        }
                    },
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'D' => ascii::digit.negate(),
            'S' => ascii::space.negate(),
            'W' => ascii::word.negate(),

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ if ascii::punct.includes(c) => CharClass::from_char(c),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Parse a character class (e.g. `[a-z]`), sans the opening bracket.
fn p_charclass(s: &mut State) -> CharClass {
    let mut classes: ~[CharClass] = ~[];

    let negate = match s.advance() {
        Some('^') => true,
        _ => { s.retreat(); false }
    };

    loop {
        match s.advance() {
            Some(c) => match c {
                ']' => break,
                '-' => match p_charclass_token(s) {
                    Some(cc_hi) => match classes.pop() {
                        Some(cc_lo) => {
                            // [a-z]
                            let lo = cc_lo.to_char().expect("bad character range");
                            let hi = cc_hi.to_char().expect("bad character range");
                            classes.push(CharClass::from_range(lo, hi));
                        },
                        None => classes.push(cc_hi)  // [-z]
                    },
                    None => classes.push(CharClass::from_char('-'))  // [a-]
                },
                _ => {
                    s.retreat();
                    classes.push(p_charclass_token(s).expect("invalid char class"));
                }
            },
            None => fail!("unexpected end of char class")
        }
    }

    let cc = CharClass::combine(classes);
    if negate {
        cc.negate()
    } else {
        cc
    }
}

fn p_charclass_token(s: &mut State) -> Option<CharClass> {
    match s.advance() {
        Some(c) => match c {
            ']' => { s.retreat(); None },
            '[' => Some(p_charclass(s)),
            '\\' => Some(p_escape(s)),
            _ => Some(CharClass::from_char(c))
        },
        None => None
    }
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}

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

    #[test]
    #[should_fail]
    fn issue_3() {
        let _ = parse("(?:a+)?");  // error: multiple repeat
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use super::{Type, Timezone, Transition, TransRule, TzError, posixtz};
use super::nom::IResult;

use std::rc::Rc;
use std::io::{Read, BufReader, Seek, SeekFrom};
use std::fs::File;
use std::path::PathBuf;
use self::byteorder::{BigEndian, ByteOrder};
use std::str;

pub fn load_timezone(timezone: &str) -> Result<Timezone, TzError> {
    let filename = match timezone {
        // os x doesn't symlink this from /usr/share/zoneinfo
        "localtime" => PathBuf::from("/etc/localtime"),
        _ => {
            let mut filename = PathBuf::from("/usr/share/zoneinfo");
            filename.push(timezone);
            filename
        }
    };
    let filename = filename.as_path();

    let f = try!(File::open(filename));
    let mut r = BufReader::new(f);

    let hdr = try!(parse_header(&mut r));
    let (trans, type_idx, types, abbrs, trule) = match hdr.version {
        1 => {
            let trans = try!(parse_trans_32(&mut r, hdr.transitions));
            let type_idx = try!(parse_type_idx(&mut r, hdr.transitions));
            let types = try!(parse_types(&mut r, hdr.types));
            let abbrs = try!(parse_abbrs(&mut r, hdr.abbr_size));
            (trans, type_idx, types, abbrs, None)
        }
        2 => {
            // We're skipping the entire v1 file since
            // at least the same data will be found in TZFile 2.
            let to_skip = hdr.transitions * 5 + hdr.types * 6 + hdr.abbr_size * 1 +
                          hdr.leaps * 4 + hdr.stdwalls * 1 +
                          hdr.utclocals * 1;
            try!(skip(&mut r, to_skip));

            // Parse the second header.
            let hdr = try!(parse_header(&mut r));
            if hdr.version != 2 {
                return Err(TzError::InvalidTzFile);
            }

            // Parse the v2 data.
            let trans = try!(parse_trans_64(&mut r, hdr.transitions));
            let type_idx = try!(parse_type_idx(&mut r, hdr.transitions));
            let types = try!(parse_types(&mut r, hdr.types));
            let abbrs = try!(parse_abbrs(&mut r, hdr.abbr_size));
            let to_skip = hdr.leaps * 8 + hdr.stdwalls * 1 + hdr.utclocals * 1;
            try!(skip(&mut r, to_skip));
            let trule = try!(parse_posix_tz(&mut r));
            (trans, type_idx, types, abbrs, Some(trule))
        }
        _ => unreachable!(),
    };

    let types: Vec<Rc<Type>> = types.iter()
                                    .map(|&(off, is_dst, abbr_idx)| {
                                        Rc::new(Type {
                                            off: off,
                                            is_dst: is_dst,
                                            abbr: abbrs.iter()
                                                       .find(|&&(idx, _)| idx == abbr_idx)
                                                       .map(|&(_, ref a)| a)
                                                       .unwrap()
                                                       .clone(),
                                        })
                                    })
                                    .collect();

    Ok(Timezone {
        trans: trans.iter()
                    .zip(type_idx.iter())
                    .map(|(&tran, &typ_idx)| {
                        Transition {
                            utc: tran as i64,
                            ttype: types[typ_idx as usize].clone(),
                        }
                    })
                    .collect(),
        trule: trule,
    })
}

struct Header {
    version: u8,
    utclocals: i32,
    stdwalls: i32,
    leaps: i32,
    transitions: i32,
    types: i32,
    abbr_size: i32,
}

fn parse_header<R: Read>(mut r: R) -> Result<Header, TzError> {
    let mut buff = [0u8; 44];
    try!(r.read_exact(&mut buff[..]));

    match str::from_utf8(&buff[..4]) {
        Ok("TZif") => (),
        _ => return Err(TzError::InvalidTzFile),
    }

    let version = match buff[4] {
        0x00 => 1,
        0x32 => 2,
        _ => return Err(TzError::UnsupportedTzFile),
    };

    Ok(Header {
        version: version,
        utclocals: BigEndian::read_i32(&buff[20..24]),
        stdwalls: BigEndian::read_i32(&buff[24..28]),
        leaps: BigEndian::read_i32(&buff[28..32]),
        transitions: BigEndian::read_i32(&buff[32..36]),
        types: BigEndian::read_i32(&buff[36..40]),
        abbr_size: BigEndian::read_i32(&buff[40..44]),
    })
}

fn parse_trans_32<R: Read>(mut r: R, n: i32) -> Result<Vec<i64>, TzError> {
    let mut trans = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 4];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        let t = BigEndian::read_i32(&buff[..]);
        trans.push(t as i64);
    }
    Ok(trans)
}

fn parse_trans_64<R: Read>(mut r: R, n: i32) -> Result<Vec<i64>, TzError> {
    let mut trans = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 8];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        let t = BigEndian::read_i64(&buff[..]);
        trans.push(t);
    }
    Ok(trans)
}

fn parse_type_idx<R: Read>(mut r: R, n: i32) -> Result<Vec<u8>, TzError> {
    let mut idx = vec![0u8; n as usize];
    try!(r.read_exact(&mut idx[..]));
    Ok(idx)
}

fn parse_types<R: Read>(mut r: R, n: i32) -> Result<Vec<(i32, bool, u8)>, TzError> {
    let mut types = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 6];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        types.push((BigEndian::read_i32(&buff[0..4]), buff[4] != 1, buff[5]));
    }
    Ok(types)
}

fn parse_abbrs<R: Read>(mut r: R, len: i32) -> Result<Vec<(u8, String)>, TzError> {
    let mut buff = vec![0u8; len as usize];
    try!(r.read_exact(&mut buff[..]));

    let mut abbrs = vec![];
    let mut idx = 0;
    for (i, _) in buff.iter().enumerate().filter(|&(_, &c)| c == 0) {
        let abbr = (&buff[idx..i]).to_owned();
        let abbr = match String::from_utf8(abbr) {
            Err(_) => return Err(TzError::InvalidTzFile),
            Ok(a) => a,
        };
        abbrs.push((idx as u8, abbr));
        idx = i + 1;
    }

    Ok(abbrs)
}

fn parse_posix_tz<R: Read>(mut r: R) -> Result<TransRule, TzError> {
    let mut s = String::new();
    try!(r.read_to_string(&mut s));

    if !s.starts_with('\n') || !s.ends_with('\n') {
        return Err(TzError::InvalidTzFile);
    }

    match posixtz(s.trim()) {
        IResult::Done(_, t) => Ok(t),
        IResult::Error(_) => Err(TzError::InvalidPosixTz),
        IResult::Incomplete(_) => Err(TzError::InvalidPosixTz),
    }
}

fn skip<R: Seek>(mut r: R, len: i32) -> Result<(), TzError> {
    try!(r.seek(SeekFrom::Current(len as i64)));
    Ok(())
}
<commit_msg>Allow parsing tz file version 3<commit_after>extern crate byteorder;

use super::{Type, Timezone, Transition, TransRule, TzError, posixtz};
use super::nom::IResult;

use std::rc::Rc;
use std::io::{Read, BufReader, Seek, SeekFrom};
use std::fs::File;
use std::path::PathBuf;
use self::byteorder::{BigEndian, ByteOrder};
use std::str;

pub fn load_timezone(timezone: &str) -> Result<Timezone, TzError> {
    let filename = match timezone {
        // os x doesn't symlink this from /usr/share/zoneinfo
        "localtime" => PathBuf::from("/etc/localtime"),
        _ => {
            let mut filename = PathBuf::from("/usr/share/zoneinfo");
            filename.push(timezone);
            filename
        }
    };
    let filename = filename.as_path();

    let f = try!(File::open(filename));
    let mut r = BufReader::new(f);

    let hdr = try!(parse_header(&mut r));
    let (trans, type_idx, types, abbrs, trule) = match hdr.version {
        1 => {
            let trans = try!(parse_trans_32(&mut r, hdr.transitions));
            let type_idx = try!(parse_type_idx(&mut r, hdr.transitions));
            let types = try!(parse_types(&mut r, hdr.types));
            let abbrs = try!(parse_abbrs(&mut r, hdr.abbr_size));
            (trans, type_idx, types, abbrs, None)
        }
        2 | 3 => {
            // We're skipping the entire v1 file since
            // at least the same data will be found in TZFile 2.
            let to_skip = hdr.transitions * 5 + hdr.types * 6 + hdr.abbr_size * 1 +
                          hdr.leaps * 4 + hdr.stdwalls * 1 +
                          hdr.utclocals * 1;
            try!(skip(&mut r, to_skip));

            // Parse the second header.
            let hdr = try!(parse_header(&mut r));
            if hdr.version != 2 && hdr.version != 3 {
                return Err(TzError::InvalidTzFile);
            }

            // Parse the v2 data.
            let trans = try!(parse_trans_64(&mut r, hdr.transitions));
            let type_idx = try!(parse_type_idx(&mut r, hdr.transitions));
            let types = try!(parse_types(&mut r, hdr.types));
            let abbrs = try!(parse_abbrs(&mut r, hdr.abbr_size));
            let to_skip = hdr.leaps * 8 + hdr.stdwalls * 1 + hdr.utclocals * 1;
            try!(skip(&mut r, to_skip));
            let trule = try!(parse_posix_tz(&mut r));
            (trans, type_idx, types, abbrs, Some(trule))
        }
        _ => unreachable!(),
    };

    let types: Vec<Rc<Type>> = types.iter()
                                    .map(|&(off, is_dst, abbr_idx)| {
                                        Rc::new(Type {
                                            off: off,
                                            is_dst: is_dst,
                                            abbr: abbrs.iter()
                                                       .find(|&&(idx, _)| idx == abbr_idx)
                                                       .map(|&(_, ref a)| a)
                                                       .unwrap()
                                                       .clone(),
                                        })
                                    })
                                    .collect();

    Ok(Timezone {
        trans: trans.iter()
                    .zip(type_idx.iter())
                    .map(|(&tran, &typ_idx)| {
                        Transition {
                            utc: tran as i64,
                            ttype: types[typ_idx as usize].clone(),
                        }
                    })
                    .collect(),
        trule: trule,
    })
}

struct Header {
    version: u8,
    utclocals: i32,
    stdwalls: i32,
    leaps: i32,
    transitions: i32,
    types: i32,
    abbr_size: i32,
}

fn parse_header<R: Read>(mut r: R) -> Result<Header, TzError> {
    let mut buff = [0u8; 44];
    try!(r.read_exact(&mut buff[..]));

    match str::from_utf8(&buff[..4]) {
        Ok("TZif") => (),
        _ => return Err(TzError::InvalidTzFile),
    }

    let version = match buff[4] {
        0x00 => 1,
        0x32 => 2,
        0x33 => 3,
        _ => return Err(TzError::UnsupportedTzFile),
    };

    Ok(Header {
        version: version,
        utclocals: BigEndian::read_i32(&buff[20..24]),
        stdwalls: BigEndian::read_i32(&buff[24..28]),
        leaps: BigEndian::read_i32(&buff[28..32]),
        transitions: BigEndian::read_i32(&buff[32..36]),
        types: BigEndian::read_i32(&buff[36..40]),
        abbr_size: BigEndian::read_i32(&buff[40..44]),
    })
}

fn parse_trans_32<R: Read>(mut r: R, n: i32) -> Result<Vec<i64>, TzError> {
    let mut trans = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 4];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        let t = BigEndian::read_i32(&buff[..]);
        trans.push(t as i64);
    }
    Ok(trans)
}

fn parse_trans_64<R: Read>(mut r: R, n: i32) -> Result<Vec<i64>, TzError> {
    let mut trans = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 8];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        let t = BigEndian::read_i64(&buff[..]);
        trans.push(t);
    }
    Ok(trans)
}

fn parse_type_idx<R: Read>(mut r: R, n: i32) -> Result<Vec<u8>, TzError> {
    let mut idx = vec![0u8; n as usize];
    try!(r.read_exact(&mut idx[..]));
    Ok(idx)
}

fn parse_types<R: Read>(mut r: R, n: i32) -> Result<Vec<(i32, bool, u8)>, TzError> {
    let mut types = Vec::with_capacity(n as usize);
    let mut buff = [0u8; 6];
    for _ in 0..n {
        try!(r.read_exact(&mut buff[..]));
        types.push((BigEndian::read_i32(&buff[0..4]), buff[4] != 1, buff[5]));
    }
    Ok(types)
}

fn parse_abbrs<R: Read>(mut r: R, len: i32) -> Result<Vec<(u8, String)>, TzError> {
    let mut buff = vec![0u8; len as usize];
    try!(r.read_exact(&mut buff[..]));

    let mut abbrs = vec![];
    let mut idx = 0;
    for (i, _) in buff.iter().enumerate().filter(|&(_, &c)| c == 0) {
        let abbr = (&buff[idx..i]).to_owned();
        let abbr = match String::from_utf8(abbr) {
            Err(_) => return Err(TzError::InvalidTzFile),
            Ok(a) => a,
        };
        abbrs.push((idx as u8, abbr));
        idx = i + 1;
    }

    Ok(abbrs)
}

fn parse_posix_tz<R: Read>(mut r: R) -> Result<TransRule, TzError> {
    let mut s = String::new();
    try!(r.read_to_string(&mut s));

    if !s.starts_with('\n') || !s.ends_with('\n') {
        return Err(TzError::InvalidTzFile);
    }

    match posixtz(s.trim()) {
        IResult::Done(_, t) => Ok(t),
        IResult::Error(_) => Err(TzError::InvalidPosixTz),
        IResult::Incomplete(_) => Err(TzError::InvalidPosixTz),
    }
}

fn skip<R: Seek>(mut r: R, len: i32) -> Result<(), TzError> {
    try!(r.seek(SeekFrom::Current(len as i64)));
    Ok(())
}
<|endoftext|>