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

use eutil::fptr_is_null;
use libc::{c_int};
use std::mem;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_list_t,cef_string_t};

//cef_string_list

#[no_mangle]
extern "C" fn cef_string_list_alloc() -> *mut cef_string_list_t {
    unsafe {
         let lt: Box<Vec<*mut cef_string_t>> = box vec!();
         mem::transmute(lt)
    }
}

#[no_mangle]
extern "C" fn cef_string_list_size(lt: *const cef_string_list_t) -> c_int {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return 0; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        v.len() as c_int
    }
}

#[no_mangle]
extern "C" fn cef_string_list_append(lt: *mut cef_string_list_t, value: *const cef_string_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        let cs = cef_string_userfree_utf8_alloc();
        cef_string_utf8_set(mem::transmute((*value).str), (*value).length, cs, 1);
        v.push(cs);
    }
}

#[no_mangle]
extern "C" fn cef_string_list_value(lt: *mut cef_string_list_t, index: c_int, value: *mut cef_string_t) -> c_int {
    unsafe {
        if index < 0 || fptr_is_null(mem::transmute(lt)) { return 0; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        if index as uint > v.len() - 1 { return 0; }
        let cs = v.get(index as uint);
        cef_string_utf8_set(mem::transmute((**cs).str), (**cs).length, value, 1)
    }
}

#[no_mangle]
extern "C" fn cef_string_list_clear(lt: *mut cef_string_list_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        if v.len() == 0 { return; }
        let mut cs;
        while v.len() != 0 {
            cs = v.pop();
            cef_string_userfree_utf8_free(cs.unwrap());
        }
    }
}

#[no_mangle]
extern "C" fn cef_string_list_free(lt: *mut cef_string_list_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        cef_string_list_clear(lt);
        drop(v);
    }
}

#[no_mangle]
extern "C" fn cef_string_list_copy(lt: *mut cef_string_list_t) -> *mut cef_string_list_t {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return 0 as *mut cef_string_list_t; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        let lt2 = cef_string_list_alloc();
        for cs in v.iter() {
            cef_string_list_append(lt2, mem::transmute((*cs)));
        }
        lt2
    }
}
<commit_msg>embedding: set string_list api functions to pub<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use eutil::fptr_is_null;
use libc::{c_int};
use std::mem;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_list_t,cef_string_t};

//cef_string_list

#[no_mangle]
pub extern "C" fn cef_string_list_alloc() -> *mut cef_string_list_t {
    unsafe {
         let lt: Box<Vec<*mut cef_string_t>> = box vec!();
         mem::transmute(lt)
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_size(lt: *const cef_string_list_t) -> c_int {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return 0; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        v.len() as c_int
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_append(lt: *mut cef_string_list_t, value: *const cef_string_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        let cs = cef_string_userfree_utf8_alloc();
        cef_string_utf8_set(mem::transmute((*value).str), (*value).length, cs, 1);
        v.push(cs);
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_value(lt: *mut cef_string_list_t, index: c_int, value: *mut cef_string_t) -> c_int {
    unsafe {
        if index < 0 || fptr_is_null(mem::transmute(lt)) { return 0; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        if index as uint > v.len() - 1 { return 0; }
        let cs = v.get(index as uint);
        cef_string_utf8_set(mem::transmute((**cs).str), (**cs).length, value, 1)
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_clear(lt: *mut cef_string_list_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        if v.len() == 0 { return; }
        let mut cs;
        while v.len() != 0 {
            cs = v.pop();
            cef_string_userfree_utf8_free(cs.unwrap());
        }
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_free(lt: *mut cef_string_list_t) {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return; }
        let mut v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        cef_string_list_clear(lt);
        drop(v);
    }
}

#[no_mangle]
pub extern "C" fn cef_string_list_copy(lt: *mut cef_string_list_t) -> *mut cef_string_list_t {
    unsafe {
        if fptr_is_null(mem::transmute(lt)) { return 0 as *mut cef_string_list_t; }
        let v: Box<Vec<*mut cef_string_t>> = mem::transmute(lt);
        let lt2 = cef_string_list_alloc();
        for cs in v.iter() {
            cef_string_list_append(lt2, mem::transmute((*cs)));
        }
        lt2
    }
}
<|endoftext|>


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

use std::process::Command;
use std::env;

fn main() {
    assert!(Command::new("make")
        .args(&["-R", "-f", "makefile.cargo"])
        .status()
        .unwrap()
        .success());
    println!("cargo:rustc-flags=-L native={}", env::var("OUT_DIR").unwrap());
}
<commit_msg>Auto merge of #190 - metajack:fix-num-jobs, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::process::Command;
use std::env;

fn main() {
    assert!(Command::new("make")
        .args(&["-R", "-f", "makefile.cargo", &format!("-j{}", env::var("NUM_JOBS").unwrap())])
        .status()
        .unwrap()
        .success());
    println!("cargo:rustc-flags=-L native={}", env::var("OUT_DIR").unwrap());
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::str::{self, FromStr};

// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
    let minor = match rustc_minor_version() {
        Some(minor) => minor,
        None => return,
    };

    let target = env::var("TARGET").unwrap();
    let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten";

    // 128-bit integers stabilized in Rust 1.26:
    // https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
    //
    // Disabled on Emscripten targets as Emscripten doesn't
    // currently support integers larger than 64 bits.
    if minor >= 26 && !emscripten {
        println!("cargo:rustc-cfg=integer128");
    }

    // #[must_use] on functions stabilized in Rust 1.27:
    // https://blog.rust-lang.org/2018/06/21/Rust-1.27.html
    if minor >= 27 {
        println!("cargo:rustc-cfg=must_use_return");
    }

    // MaybeUninit<T> stabilized in Rust 1.36:
    // https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html
    if minor >= 36 {
        println!("cargo:rustc-cfg=maybe_uninit");
    }
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    let next = pieces.next()?;
    u32::from_str(next).ok()
}
<commit_msg>Remove check for rustc <1.26<commit_after>use std::env;
use std::process::Command;
use std::str::{self, FromStr};

// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
    let minor = match rustc_minor_version() {
        Some(minor) => minor,
        None => return,
    };

    let target = env::var("TARGET").unwrap();
    let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten";

    // 128-bit integers disabled on Emscripten targets as Emscripten doesn't
    // currently support integers larger than 64 bits.
    if !emscripten {
        println!("cargo:rustc-cfg=integer128");
    }

    // #[must_use] on functions stabilized in Rust 1.27:
    // https://blog.rust-lang.org/2018/06/21/Rust-1.27.html
    if minor >= 27 {
        println!("cargo:rustc-cfg=must_use_return");
    }

    // MaybeUninit<T> stabilized in Rust 1.36:
    // https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html
    if minor >= 36 {
        println!("cargo:rustc-cfg=maybe_uninit");
    }
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    let next = pieces.next()?;
    u32::from_str(next).ok()
}
<|endoftext|>


<commit_before>fn main() {
    println!("cargo:rustc-link-search=framework=/usr/local/opt/qt5/Frameworks");
    println!("cargo:rustc-link-lib=c++");
}
<commit_msg>Make the QT location more dynamic<commit_after>use std::env;
use std::env::consts;

fn main() {

    // We use the QTDIR or QTDIR64 env variables to find the location of
    // Qt5. If these are not set, we use the default homebrew install
    // location.
    let qtdir_variable = match consts::ARCH {
        "x86_64" => "QTDIR64",
        _ => "QTDIR",
    };
    let qt5_location = env::var(qtdir_variable).unwrap_or(String::from("/usr/local/opt/qt5"));

    println!("cargo:rustc-link-search=framework={}/Frameworks", qt5_location);
    println!("cargo:rustc-link-lib=c++");
}
<|endoftext|>


<commit_before>use std::io::fs::{rmdir_recursive};
use core::{SourceId};
use util::{CargoResult, human, ChainError};
use ops::{read_manifest};
use std::io::{File};
use util::toml::{project_layout};

/// Cleans the project from build artifacts.

pub fn clean(manifest_path: &Path) -> CargoResult<()>
{
    let mut file = try!(File::open(manifest_path));
    let data = try!(file.read_to_end());
    let layout = project_layout(&manifest_path.dir_path());
    let (manifest, _) = try!(read_manifest(data.as_slice(),
                                           layout,
                                           &SourceId::for_path(manifest_path)));

    let build_dir = manifest.get_target_dir();

    if build_dir.exists() {
      return rmdir_recursive(build_dir).chain_error(|| human("Could not remove build directory"))
    }

    Ok(())
}
<commit_msg>remove unecessary return statement<commit_after>use std::io::fs::{rmdir_recursive};
use core::{SourceId};
use util::{CargoResult, human, ChainError};
use ops::{read_manifest};
use std::io::{File};
use util::toml::{project_layout};

/// Cleans the project from build artifacts.

pub fn clean(manifest_path: &Path) -> CargoResult<()>
{
    let mut file = try!(File::open(manifest_path));
    let data = try!(file.read_to_end());
    let layout = project_layout(&manifest_path.dir_path());
    let (manifest, _) = try!(read_manifest(data.as_slice(),
                                           layout,
                                           &SourceId::for_path(manifest_path)));

    let build_dir = manifest.get_target_dir();

    if build_dir.exists() {
      rmdir_recursive(build_dir).chain_error(|| human("Could not remove build directory"))
    } else {
      Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
use rustc_hir as hir;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `drop_bounds` lint checks for generics with `std::ops::Drop` as
    /// bounds.
    ///
    /// ### Example
    ///
    /// ```rust
    /// fn foo<T: Drop>() {}
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// A generic trait bound of the form `T: Drop` is most likely misleading
    /// and not what the programmer intended (they probably should have used
    /// `std::mem::needs_drop` instead).
    ///
    /// `Drop` bounds do not actually indicate whether a type can be trivially
    /// dropped or not, because a composite type containing `Drop` types does
    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
    /// to write an implementation that assumes that a type can be trivially
    /// dropped while also supplying a specialization for `T: Drop` that
    /// actually calls the destructor. However, this breaks down e.g. when `T`
    /// is `String`, which does not implement `Drop` itself but contains a
    /// `Vec`, which does implement `Drop`, so assuming `T` can be trivially
    /// dropped would lead to a memory leak here.
    ///
    /// Furthermore, the `Drop` trait only contains one method, `Drop::drop`,
    /// which may not be called explicitly in user code (`E0040`), so there is
    /// really no use case for using `Drop` in trait bounds, save perhaps for
    /// some obscure corner cases, which can use `#[allow(drop_bounds)]`.
    pub DROP_BOUNDS,
    Warn,
    "bounds of the form `T: Drop` are most likely incorrect"
}

declare_lint! {
    /// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// fn foo(_x: Box<dyn Drop>) {}
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// A trait object bound of the form `dyn Drop` is most likely misleading
    /// and not what the programmer intended.
    ///
    /// `Drop` bounds do not actually indicate whether a type can be trivially
    /// dropped or not, because a composite type containing `Drop` types does
    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
    /// to write a deferred drop system, to pull cleaning up memory out of a
    /// latency-sensitive code path, using `dyn Drop` trait objects. However,
    /// this breaks down e.g. when `T` is `String`, which does not implement
    /// `Drop`, but should probably be accepted.
    ///
    /// To write a trait object bound that accepts anything, use a placeholder
    /// trait with a blanket implementation.
    ///
    /// ```rust
    /// trait Placeholder {}
    /// impl<T> Placeholder for T {}
    /// fn foo(_x: Box<dyn Placeholder>) {}
    /// ```
    pub DYN_DROP,
    Warn,
    "trait objects of the form `dyn Drop` are useless"
}

declare_lint_pass!(
    /// Lint for bounds of the form `T: Drop`, which usually
    /// indicate an attempt to emulate `std::mem::needs_drop`.
    DropTraitConstraints => [DROP_BOUNDS, DYN_DROP]
);

impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
        use rustc_middle::ty::PredicateKind::*;

        let predicates = cx.tcx.explicit_predicates_of(item.def_id);
        for &(predicate, span) in predicates.predicates {
            let trait_predicate = match predicate.kind().skip_binder() {
                Trait(trait_predicate) => trait_predicate,
                _ => continue,
            };
            let def_id = trait_predicate.trait_ref.def_id;
            if cx.tcx.lang_items().drop_trait() == Some(def_id) {
                // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
                if trait_predicate.trait_ref.self_ty().is_impl_trait() {
                    continue;
                }
                cx.struct_span_lint(DROP_BOUNDS, span, |lint| {
                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
                        Some(needs_drop) => needs_drop,
                        None => return,
                    };
                    let msg = format!(
                        "bounds on `{}` are most likely incorrect, consider instead \
                         using `{}` to detect whether a type can be trivially dropped",
                        predicate,
                        cx.tcx.def_path_str(needs_drop)
                    );
                    lint.build(&msg).emit()
                });
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
        let bounds = match &ty.kind {
            hir::TyKind::TraitObject(bounds, _lifetime, _syntax) => bounds,
            _ => return,
        };
        for bound in &bounds[..] {
            let def_id = bound.trait_ref.trait_def_id();
            if cx.tcx.lang_items().drop_trait() == def_id {
                cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
                        Some(needs_drop) => needs_drop,
                        None => return,
                    };
                    let msg = format!(
                        "types that do not implement `Drop` can still have drop glue, consider \
                        instead using `{}` to detect whether a type is trivially dropped",
                        cx.tcx.def_path_str(needs_drop)
                    );
                    lint.build(&msg).emit()
                });
            }
        }
    }
}
<commit_msg>Do not lint for ~const Drop bounds<commit_after>use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
use rustc_hir as hir;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `drop_bounds` lint checks for generics with `std::ops::Drop` as
    /// bounds.
    ///
    /// ### Example
    ///
    /// ```rust
    /// fn foo<T: Drop>() {}
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// A generic trait bound of the form `T: Drop` is most likely misleading
    /// and not what the programmer intended (they probably should have used
    /// `std::mem::needs_drop` instead).
    ///
    /// `Drop` bounds do not actually indicate whether a type can be trivially
    /// dropped or not, because a composite type containing `Drop` types does
    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
    /// to write an implementation that assumes that a type can be trivially
    /// dropped while also supplying a specialization for `T: Drop` that
    /// actually calls the destructor. However, this breaks down e.g. when `T`
    /// is `String`, which does not implement `Drop` itself but contains a
    /// `Vec`, which does implement `Drop`, so assuming `T` can be trivially
    /// dropped would lead to a memory leak here.
    ///
    /// Furthermore, the `Drop` trait only contains one method, `Drop::drop`,
    /// which may not be called explicitly in user code (`E0040`), so there is
    /// really no use case for using `Drop` in trait bounds, save perhaps for
    /// some obscure corner cases, which can use `#[allow(drop_bounds)]`.
    pub DROP_BOUNDS,
    Warn,
    "bounds of the form `T: Drop` are most likely incorrect"
}

declare_lint! {
    /// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// fn foo(_x: Box<dyn Drop>) {}
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// A trait object bound of the form `dyn Drop` is most likely misleading
    /// and not what the programmer intended.
    ///
    /// `Drop` bounds do not actually indicate whether a type can be trivially
    /// dropped or not, because a composite type containing `Drop` types does
    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
    /// to write a deferred drop system, to pull cleaning up memory out of a
    /// latency-sensitive code path, using `dyn Drop` trait objects. However,
    /// this breaks down e.g. when `T` is `String`, which does not implement
    /// `Drop`, but should probably be accepted.
    ///
    /// To write a trait object bound that accepts anything, use a placeholder
    /// trait with a blanket implementation.
    ///
    /// ```rust
    /// trait Placeholder {}
    /// impl<T> Placeholder for T {}
    /// fn foo(_x: Box<dyn Placeholder>) {}
    /// ```
    pub DYN_DROP,
    Warn,
    "trait objects of the form `dyn Drop` are useless"
}

declare_lint_pass!(
    /// Lint for bounds of the form `T: Drop`, which usually
    /// indicate an attempt to emulate `std::mem::needs_drop`.
    DropTraitConstraints => [DROP_BOUNDS, DYN_DROP]
);

impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
        use rustc_middle::ty::PredicateKind::*;
        use rustc_middle::ty;

        let predicates = cx.tcx.explicit_predicates_of(item.def_id);
        for &(predicate, span) in predicates.predicates {
            let trait_predicate = match predicate.kind().skip_binder() {
                Trait(trait_predicate) => trait_predicate,
                _ => continue,
            };
            if trait_predicate.constness == ty::BoundConstness::ConstIfConst {
                // `~const Drop` definitely have meanings so avoid linting here.
                continue
            }
            let def_id = trait_predicate.trait_ref.def_id;
            if cx.tcx.lang_items().drop_trait() == Some(def_id) {
                // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
                if trait_predicate.trait_ref.self_ty().is_impl_trait() {
                    continue;
                }
                cx.struct_span_lint(DROP_BOUNDS, span, |lint| {
                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
                        Some(needs_drop) => needs_drop,
                        None => return,
                    };
                    let msg = format!(
                        "bounds on `{}` are most likely incorrect, consider instead \
                         using `{}` to detect whether a type can be trivially dropped",
                        predicate,
                        cx.tcx.def_path_str(needs_drop)
                    );
                    lint.build(&msg).emit()
                });
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
        let bounds = match &ty.kind {
            hir::TyKind::TraitObject(bounds, _lifetime, _syntax) => bounds,
            _ => return,
        };
        for bound in &bounds[..] {
            let def_id = bound.trait_ref.trait_def_id();
            if cx.tcx.lang_items().drop_trait() == def_id {
                cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
                        Some(needs_drop) => needs_drop,
                        None => return,
                    };
                    let msg = format!(
                        "types that do not implement `Drop` can still have drop glue, consider \
                        instead using `{}` to detect whether a type is trivially dropped",
                        cx.tcx.def_path_str(needs_drop)
                    );
                    lint.build(&msg).emit()
                });
            }
        }
    }
}
<|endoftext|>


<commit_before>//#![feature(trace_macros)]
#![feature(test)]
extern crate test;

#[macro_use]
extern crate nom;

use nom::{digit, alphanumeric};

use test::Bencher;

use std::str::{self, FromStr};
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Num(f32),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

// FIXME: since we already parsed a serie of digits and dots,
// we know it is correct UTF-8. no need to use from_utf8 to
// verify it is correct
// FIXME: use alt_complete (implement ws for alt_complete)
named!(unsigned_float <f32>, map_res!(
  map_res!(
    recognize!(
      alt_complete!(
        delimited!(digit, tag!("."), opt!(complete!(digit))) |
        delimited!(opt!(digit), tag!("."), digit)            |
        digit
      )
    ),
    str::from_utf8
  ),
  FromStr::from_str
));
named!(float<f32>, map!(
  pair!(
    opt!(alt!(tag!("+") | tag!("-"))),
    unsigned_float
  ),
  |(sign, value): (Option<&[u8]>, f32)| {
    sign.and_then(|s| if s[0] == (b'-') { Some(-1f32) } else { None }).unwrap_or(1f32) * value
  }
));

//FIXME: verify how json strings are formatted
named!(string<&str>,
  delimited!(
    tag!("\""),
    map_res!(escaped!(call!(alphanumeric), '\\', is_a!("\"n\\")), str::from_utf8),
    tag!("\"")
  )
);

named!(array < Vec<JsonValue> >,
  ws!(
    delimited!(
      tag!("["),
      separated_list!(tag!(","), value),
      tag!("]")
    )
  )
);

named!(key_value<(&str,JsonValue)>,
  ws!(
    separated_pair!(
      string,
      tag!(":"),
      value
    )
  )
);

named!(hash< HashMap<String,JsonValue> >,
  ws!(
    map!(
      delimited!(
        tag!("{"),
        separated_list!(tag!(","), key_value),
        tag!("}")
        ),
      |tuple_vec| {
        let mut h: HashMap<String, JsonValue> = HashMap::new();
        for (k, v) in tuple_vec {
          h.insert(String::from(k), v);
        }
        h
      }
    )
  )
);

named!(value<JsonValue>,
  ws!(
    alt!(
      hash   => { |h|   JsonValue::Object(h)            } |
      array  => { |v|   JsonValue::Array(v)             } |
      string => { |s|   JsonValue::Str(String::from(s)) } |
      float  => { |num| JsonValue::Num(num)             }
    )
  )
);

#[bench]
fn json_bench(b: &mut Bencher) {
  let data = &b"  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  }  ";

  //println!("data:\n{:?}", value(&data[..]));
  b.iter(|| value(&data[..]));
}
<commit_msg>add benchmarks for floats<commit_after>//#![feature(trace_macros)]
#![feature(test)]
extern crate test;

#[macro_use]
extern crate nom;

use nom::{digit, alphanumeric};

use test::Bencher;

use std::str::{self, FromStr};
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Num(f32),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

// FIXME: since we already parsed a serie of digits and dots,
// we know it is correct UTF-8. no need to use from_utf8 to
// verify it is correct
// FIXME: use alt_complete (implement ws for alt_complete)
named!(unsigned_float <f32>, map_res!(
  map_res!(
    recognize!(
      alt_complete!(
        delimited!(digit, tag!("."), opt!(complete!(digit))) |
        delimited!(opt!(digit), tag!("."), digit)            |
        digit
      )
    ),
    str::from_utf8
  ),
  FromStr::from_str
));
named!(float<f32>, map!(
  pair!(
    opt!(alt!(tag!("+") | tag!("-"))),
    unsigned_float
  ),
  |(sign, value): (Option<&[u8]>, f32)| {
    sign.and_then(|s| if s[0] == (b'-') { Some(-1f32) } else { None }).unwrap_or(1f32) * value
  }
));

//FIXME: verify how json strings are formatted
named!(string<&str>,
  delimited!(
    tag!("\""),
    map_res!(escaped!(call!(alphanumeric), '\\', is_a!("\"n\\")), str::from_utf8),
    tag!("\"")
  )
);

named!(array < Vec<JsonValue> >,
  ws!(
    delimited!(
      tag!("["),
      separated_list!(tag!(","), value),
      tag!("]")
    )
  )
);

named!(key_value<(&str,JsonValue)>,
  ws!(
    separated_pair!(
      string,
      tag!(":"),
      value
    )
  )
);

named!(hash< HashMap<String,JsonValue> >,
  ws!(
    map!(
      delimited!(
        tag!("{"),
        separated_list!(tag!(","), key_value),
        tag!("}")
        ),
      |tuple_vec| {
        let mut h: HashMap<String, JsonValue> = HashMap::new();
        for (k, v) in tuple_vec {
          h.insert(String::from(k), v);
        }
        h
      }
    )
  )
);

named!(value<JsonValue>,
  ws!(
    alt!(
      hash   => { |h|   JsonValue::Object(h)            } |
      array  => { |v|   JsonValue::Array(v)             } |
      string => { |s|   JsonValue::Str(String::from(s)) } |
      float  => { |num| JsonValue::Num(num)             }
    )
  )
);

#[bench]
fn json_bench(b: &mut Bencher) {
  let data = &b"  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  }  ";

  //println!("data:\n{:?}", value(&data[..]));
  b.iter(|| value(&data[..]));
}

#[bench]
fn recognize_float_bytes(b: &mut Bencher) {
  use nom::recognize_float;
  b.iter(|| recognize_float(&b"-1.234E-12"[..]));
}

#[bench]
fn recognize_float_str(b: &mut Bencher) {
  use nom::recognize_float;
  b.iter(|| recognize_float("-1.234E-12"));
}

#[bench]
fn float_bytes(b: &mut Bencher) {
  use nom::float;
  b.iter(|| float(&b"-1.234E-12"[..]));
}

#[bench]
fn float_str(b: &mut Bencher) {
  use nom::float_s;
  b.iter(|| float_s("-1.234E-12"));
}
<|endoftext|>


<commit_before>//!
//! Values manipulated by endpoints
//!

use std::time::Duration;
use std::cmp::{PartialOrd, Ordering};

extern crate chrono;
use self::chrono::{DateTime, Local};

extern crate serde_json;

///
/// The type of values manipulated by endpoints.
///
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Type {
    ///
    /// # Trivial values
    ///

    /// An empty value. Used for instance to inform that a countdown
    /// has reached 0 or that a device is ready.
    Unit,

    /// A boolean. Used for instance for on-off switches, presence
    /// detectors, etc.
    Bool,

    ///
    /// # Time
    ///

    /// A duration. Used for instance in countdowns.
    Duration,

    /// A precise timestamp. Used for instance to determine when an
    /// event has taken place.
    TimeStamp,

    Temperature,

    ///
    /// ...
    ///
    Color,
    Json,
    Binary,
    ExtNumeric,
}

/// A temperature. Internal representation may be either Fahrenheit or
/// Celcius. The FoxBox adapters are expected to perform conversions
/// to the format requested by their devices.
#[derive(Debug, Clone, PartialEq)]
pub enum Temperature {
    /// Fahrenheit
    F(f64),
    /// Celcius
    C(f64),
}

impl Temperature {
    /// Get a temperature in Fahrenheit.
    pub fn as_f(&self) -> f64 {
        unimplemented!();
    }

    /// Get a temperature in Celcius.
    pub fn as_c(&self) -> f64 {
        unimplemented!();
    }
}

impl PartialOrd for Temperature {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.as_c().partial_cmp(&other.as_c())
    }
}

/// A color. Internal representation may vary. The FoxBox adapters are
/// expected to perform conversions to the format requested by their
/// device.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Color {
    RGBA(f64, f64, f64, f64, f64)
}

/// Representation of an object in JSON. It is often (albeit not
/// always) possible to choose a more precise data structure for
/// representing values send/accepted by a service. If possible,
/// adapters should rather pick such more precise data structure.
#[derive(Debug, Clone, PartialEq)]
pub struct Json(serde_json::value::Value);

impl PartialOrd for Json {
    /// Two Json objects are never comparable to each other.
    fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
        None
    }
}

/// A data structure holding a numeric value of a type that has not
/// been standardized yet.
#[derive(Debug, Clone, PartialEq)]
pub struct ExtNumeric {
    pub value: f64,

    /// The vendor. Used for namespacing purposes, to avoid
    /// confusing two incompatible extensions with similar
    /// names. For instance, "foxlink@mozilla.com".
    pub vendor: String,

    /// Identification of the adapter introducing this value.
    /// Designed to aid with tracing and debugging.
    pub adapter: String,

    /// A string describing the nature of the value, designed to
    /// aid with type-checking.
    ///
    /// Examples: `"GroundHumidity"`.
    pub kind: String,
}

impl PartialOrd for ExtNumeric {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.vendor != other.vendor {
            return None;
        } else if self.kind != other.kind {
            return None;
        } else {
            self.value.partial_cmp(&other.value)
        }
    }
}

/// Base definitions for actual values. Most API clients will rather
/// use `Value`.
///
/// Splitting the defintion between `Value` and `ValueBase` is mostly
/// a convenience to refine the automatically-derived `PartialOrd` on
/// `ValueBase` into something that will never compare two values with
/// different types.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ValueBase {
    Unit,
    Bool(bool),
    Duration(Duration),
    TimeStamp(chrono::DateTime<Local>),
    Temperature(Temperature),
    Color(Color),

    // FIXME: Add more as we identify needs

    /// A numeric value representing a unit that has not been
    /// standardized yet into the API.
    ExtNumeric(ExtNumeric),
    Json(Json),
    Binary {data: Vec<u8>, mimetype: String}
}

impl ValueBase {
    pub fn get_type(&self) -> Type {
        match *self {
            ValueBase::Unit => Type::Unit,
            ValueBase::Bool(_) => Type::Bool,
            ValueBase::Duration(_) => Type::Duration,
            ValueBase::TimeStamp(_) => Type::TimeStamp,
            ValueBase::Temperature(_) => Type::Temperature,
            ValueBase::Color(_) => Type::Color,
            ValueBase::Json(_) => Type::Json,
            ValueBase::Binary{..} => Type::Binary,
            ValueBase::ExtNumeric(_) => Type::ExtNumeric,
        }
    }
}

/// Representation of an actual value that can be sent to/received
/// from a service.
#[derive(Debug, Clone, PartialEq)]
pub struct Value(ValueBase);

impl Value {
    /// Get the type attached to a value.
    pub fn get_type(&self) -> Type {
        self.0.get_type()
    }
}

impl PartialOrd for Value {
    /// Two values of the same type can be compared using the usual
    /// comparison for values of this type. Two values of distinct
    /// types cannot be compared.
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.get_type() != other.get_type() {
            None
        } else {
            self.0.partial_cmp(&other.0)
        }
    }
}
<commit_msg>[fxbox/taxonomy] Merged ValueBase and Value<commit_after>//!
//! Values manipulated by endpoints
//!

use std::time::Duration;
use std::cmp::{PartialOrd, Ordering};

extern crate chrono;
use self::chrono::{DateTime, Local};

extern crate serde_json;

///
/// The type of values manipulated by endpoints.
///
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub enum Type {
    ///
    /// # Trivial values
    ///

    /// An empty value. Used for instance to inform that a countdown
    /// has reached 0 or that a device is ready.
    Unit,

    /// A boolean. Used for instance for on-off switches, presence
    /// detectors, etc.
    Bool,

    ///
    /// # Time
    ///

    /// A duration. Used for instance in countdowns.
    Duration,

    /// A precise timestamp. Used for instance to determine when an
    /// event has taken place.
    TimeStamp,

    Temperature,

    ///
    /// ...
    ///
    Color,
    Json,
    Binary,
    ExtNumeric,
}

/// A temperature. Internal representation may be either Fahrenheit or
/// Celcius. The FoxBox adapters are expected to perform conversions
/// to the format requested by their devices.
#[derive(Debug, Clone, PartialEq)]
pub enum Temperature {
    /// Fahrenheit
    F(f64),
    /// Celcius
    C(f64),
}

impl Temperature {
    /// Get a temperature in Fahrenheit.
    pub fn as_f(&self) -> f64 {
        unimplemented!();
    }

    /// Get a temperature in Celcius.
    pub fn as_c(&self) -> f64 {
        unimplemented!();
    }
}

impl PartialOrd for Temperature {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.as_c().partial_cmp(&other.as_c())
    }
}

/// A color. Internal representation may vary. The FoxBox adapters are
/// expected to perform conversions to the format requested by their
/// device.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Color {
    RGBA(f64, f64, f64, f64, f64)
}

/// Representation of an object in JSON. It is often (albeit not
/// always) possible to choose a more precise data structure for
/// representing values send/accepted by a service. If possible,
/// adapters should rather pick such more precise data structure.
#[derive(Debug, Clone, PartialEq)]
pub struct Json(serde_json::value::Value);

impl PartialOrd for Json {
    /// Two Json objects are never comparable to each other.
    fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
        None
    }
}

/// A data structure holding a numeric value of a type that has not
/// been standardized yet.
#[derive(Debug, Clone, PartialEq)]
pub struct ExtNumeric {
    pub value: f64,

    /// The vendor. Used for namespacing purposes, to avoid
    /// confusing two incompatible extensions with similar
    /// names. For instance, "foxlink@mozilla.com".
    pub vendor: String,

    /// Identification of the adapter introducing this value.
    /// Designed to aid with tracing and debugging.
    pub adapter: String,

    /// A string describing the nature of the value, designed to
    /// aid with type-checking.
    ///
    /// Examples: `"GroundHumidity"`.
    pub kind: String,
}

impl PartialOrd for ExtNumeric {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.vendor != other.vendor {
            return None;
        } else if self.kind != other.kind {
            return None;
        } else {
            self.value.partial_cmp(&other.value)
        }
    }
}

/// Representation of an actual value that can be sent to/received
/// from a service.
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Unit,
    Bool(bool),
    Duration(Duration),
    TimeStamp(chrono::DateTime<Local>),
    Temperature(Temperature),
    Color(Color),

    // FIXME: Add more as we identify needs

    /// A numeric value representing a unit that has not been
    /// standardized yet into the API.
    ExtNumeric(ExtNumeric),
    Json(Json),
    Binary {data: Vec<u8>, mimetype: String}
}

impl Value {
    pub fn get_type(&self) -> Type {
        match *self {
            Value::Unit => Type::Unit,
            Value::Bool(_) => Type::Bool,
            Value::Duration(_) => Type::Duration,
            Value::TimeStamp(_) => Type::TimeStamp,
            Value::Temperature(_) => Type::Temperature,
            Value::Color(_) => Type::Color,
            Value::Json(_) => Type::Json,
            Value::Binary{..} => Type::Binary,
            Value::ExtNumeric(_) => Type::ExtNumeric,
        }
    }
}

impl PartialOrd for Value {
    /// Two values of the same type can be compared using the usual
    /// comparison for values of this type. Two values of distinct
    /// types cannot be compared.
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        use self::Value::*;
        use std::cmp::Ordering::*;
        match (self, other) {
            (&Unit, &Unit) => Some(Equal),
            (&Unit, _) => None,

            (&Bool(a), &Bool(b)) => a.partial_cmp(&b),
            (&Bool(_), _) => None,

            (&Duration(ref a), &Duration(ref b)) => a.partial_cmp(b),
            (&Duration(_), _) => None,

            (&TimeStamp(ref a), &TimeStamp(ref b)) => a.partial_cmp(b),
            (&TimeStamp(_), _) => None,

            (&Temperature(ref a), &Temperature(ref b)) => a.partial_cmp(b),
            (&Temperature(_), _) => None,

            (&Color(ref a), &Color(ref b)) => a.partial_cmp(b),
            (&Color(_), _) => None,

            (&ExtNumeric(ref a), &ExtNumeric(ref b)) => a.partial_cmp(b),
            (&ExtNumeric(_), _) => None,

            (&Json(ref a), &Json(ref b)) => a.partial_cmp(b),
            (&Json(_), _) => None,

            (a@&Binary{..}, b@&Binary{..}) => a.partial_cmp(&b),
            (&Binary{..}, _) => None,
        }
    }
}
<|endoftext|>


<commit_before>pub mod c4fm;
pub mod fir;
pub mod filters;
pub mod bits;
<commit_msg>Sort lib modules<commit_after>pub mod bits;
pub mod c4fm;
pub mod filters;
pub mod fir;
<|endoftext|>


<commit_before>mod grammar {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

use chrono::Duration;
use chrono::prelude::*;
use std::error::Error;
use std::fmt;
use util::Rounding;

#[derive(Debug)]
pub enum ParseError {
    PE(grammar::ParseError),
    Overflow,
    Hour,
    Minute,
    Second,
    Nanosecond,
}

impl From<grammar::ParseError> for ParseError {
    fn from(err: grammar::ParseError) -> ParseError {
        ParseError::PE(err)
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::PE(ref err) => err.description(),
            ParseError::Overflow => "Invalid input",
            ParseError::Hour => "The specified hours is invalid",
            ParseError::Minute => "The specified minutes is invalid",
            ParseError::Second => "The specified seconds is invalid",
            ParseError::Nanosecond => "The specified nanoseconds is invalid",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::PE(ref err) => Some(err as &Error),
            _ => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::PE(ref err) => fmt::Display::fmt(err, f),
            _ => fmt::Display::fmt(self.description(), f),
        }
    }
}

pub fn parse_rounding(fmt: &str) -> Result<Rounding, ParseError> {
    let res = try!(grammar::rounding(fmt));
    return Ok(res);
}

pub fn parse_offset(offset: &str,
                    time: DateTime<FixedOffset>)
                    -> Result<DateTime<FixedOffset>, ParseError> {
    let offset = try!(grammar::offset(offset));
    let offset = Duration::seconds(offset as i64);
    time.checked_add_signed(offset).ok_or(ParseError::Overflow)

}

pub fn parse_time(input: &str, time: DateTime<FixedOffset>) -> Result<DateTime<FixedOffset>, ParseError> {
    let (hour, minute, second) = try!(grammar::time(input));
    let nanosecond = second.fract() * 1_000_000_000.0;
    let time = try!(time.with_hour(hour).ok_or(ParseError::Hour));
    let time = try!(time.with_minute(minute).ok_or(ParseError::Minute));
    let time = try!(time.with_second(second as u32).ok_or(ParseError::Second));
    let time = try!(time.with_nanosecond(nanosecond as u32).ok_or(ParseError::Nanosecond));
    Ok(time)
}

pub fn parse_datetime(input: &str, time: DateTime<FixedOffset>) -> Result<DateTime<FixedOffset>, ParseError> {
    let (year, month, day, hour, minute, second, tz) = try!(grammar::datetime(input));

    let year = year.unwrap_or(time.year());
    let month = month.unwrap_or(time.month());
    let day = day.unwrap_or(time.day());

    //set timezone
    let tz = tz.unwrap_or(time.offset().utc_minus_local());
    let tzo = FixedOffset::west(tz);
    let time = tzo.ymd(year, month, day);

    // set the time
    let time = time.and_hms(0,0,0);
    let time = try!(time.with_hour(hour).ok_or(ParseError::Hour));
    let time = try!(time.with_minute(minute).ok_or(ParseError::Minute));
    let time = try!(time.with_second(second as u32).ok_or(ParseError::Second));
    let nanosecond = second.fract() * 1_000_000_000.0;
    let time = try!(time.with_nanosecond(nanosecond as u32).ok_or(ParseError::Nanosecond));
    Ok(time)
}

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

    #[test]
    fn parse_rounding_test() {
        assert_eq!(parse_rounding("+15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("U15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("u15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("7.5").unwrap(), Rounding::Up(450.0));

        assert_eq!(parse_rounding("-30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("D30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("d7.5m").unwrap(), Rounding::Down(450.0));

        assert_eq!(parse_rounding("=1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("H1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("h7.5m").unwrap(), Rounding::Half(450.0));

        assert_eq!(parse_rounding("+30S").unwrap(), Rounding::Up(30.0));
        assert_eq!(parse_rounding("D30s").unwrap(), Rounding::Down(30.0));
        assert_eq!(parse_rounding("h30s").unwrap(), Rounding::Half(30.0));
        assert_eq!(parse_rounding("H1d").unwrap(), Rounding::Half(86400.0));
    }

    #[test]
    fn offset_test() {
        assert_eq!(grammar::offset("+2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("-1.55h").unwrap(), -5580.0);
        assert_eq!(grammar::offset("+2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("_2:22").unwrap(), -8520.0);
    }

    #[test]
    fn parse_offset_test() {
        let ctime = now();
        let n = ctime.checked_add_signed(Duration::seconds(5580)).unwrap();
        assert_eq!(parse_offset("+1.55h", ctime).unwrap(), n);
    }

    #[test]
    fn time_test() {
        assert_eq!(grammar::time("09:22:32").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32.055").unwrap(), (9, 22, 32.055));
        assert_eq!(grammar::time("9:22:32 Am").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32.055Pm").unwrap(), (21, 22, 32.055));
        assert_eq!(grammar::time("9:22 Pm").unwrap(), (21, 22, 0.0));
        // Might want to fail on these someday
        assert_eq!(grammar::time("9:22:32. Am").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22: Pm").unwrap(), (21, 22, 0.0));
        assert_eq!(grammar::time("99:22:32").unwrap(), (99, 22, 32.0));
    }

    #[test]
    fn parse_time_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_time("9:22:31.9617 Pm", ctime).unwrap().to_rfc3339(), "2017-04-30T21:22:31.961700416-05:00");
        assert_eq!(parse_time("9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
    }

    #[test]
    fn datetime_test() {
        assert_eq!(grammar::datetime("4/2 9:22 Pm").unwrap(), (None, Some(4), Some(2), 21, 22, 0.0, None));
        assert_eq!(grammar::datetime("2017-4/2 9:22 Pm").unwrap(), (Some(2017), Some(4), Some(2), 21, 22, 0.0, None));
        assert_eq!(grammar::datetime("2017-4/2").unwrap(), (Some(2017), Some(4), Some(2), 0, 0, 0.0, None));
        assert_eq!(grammar::datetime("9:22 Pm").unwrap(), (None, None, None, 21, 22, 0.0, None));
    }

    #[test]
    fn parse_datetime_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_datetime("9:22:31.9617 Pm", ctime).unwrap().to_rfc3339(), "2017-04-30T21:22:31.961700416-05:00");
        assert_eq!(parse_datetime("9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
        assert_eq!(parse_datetime("2017-4-30 9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
        assert_eq!(parse_datetime("2017-4-30", ctime).unwrap().to_rfc3339(), "2017-04-30T00:00:00-05:00");
    }

    #[test]
    #[should_panic(expected = "ParseError")]
    fn parse_datetime_seperator_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_datetime("2017-4-309:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
    }
}
<commit_msg>Allow dead code on parse_time()<commit_after>mod grammar {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

use chrono::Duration;
use chrono::prelude::*;
use std::error::Error;
use std::fmt;
use util::Rounding;

#[derive(Debug)]
pub enum ParseError {
    PE(grammar::ParseError),
    Overflow,
    Hour,
    Minute,
    Second,
    Nanosecond,
}

impl From<grammar::ParseError> for ParseError {
    fn from(err: grammar::ParseError) -> ParseError {
        ParseError::PE(err)
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::PE(ref err) => err.description(),
            ParseError::Overflow => "Invalid input",
            ParseError::Hour => "The specified hours is invalid",
            ParseError::Minute => "The specified minutes is invalid",
            ParseError::Second => "The specified seconds is invalid",
            ParseError::Nanosecond => "The specified nanoseconds is invalid",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::PE(ref err) => Some(err as &Error),
            _ => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::PE(ref err) => fmt::Display::fmt(err, f),
            _ => fmt::Display::fmt(self.description(), f),
        }
    }
}

pub fn parse_rounding(fmt: &str) -> Result<Rounding, ParseError> {
    let res = try!(grammar::rounding(fmt));
    return Ok(res);
}

pub fn parse_offset(offset: &str,
                    time: DateTime<FixedOffset>)
                    -> Result<DateTime<FixedOffset>, ParseError> {
    let offset = try!(grammar::offset(offset));
    let offset = Duration::seconds(offset as i64);
    time.checked_add_signed(offset).ok_or(ParseError::Overflow)

}

#[allow(dead_code)] // This function may go away if I don't find a use for it soon
pub fn parse_time(input: &str, time: DateTime<FixedOffset>) -> Result<DateTime<FixedOffset>, ParseError> {
    let (hour, minute, second) = try!(grammar::time(input));
    let nanosecond = second.fract() * 1_000_000_000.0;
    let time = try!(time.with_hour(hour).ok_or(ParseError::Hour));
    let time = try!(time.with_minute(minute).ok_or(ParseError::Minute));
    let time = try!(time.with_second(second as u32).ok_or(ParseError::Second));
    let time = try!(time.with_nanosecond(nanosecond as u32).ok_or(ParseError::Nanosecond));
    Ok(time)
}

pub fn parse_datetime(input: &str, time: DateTime<FixedOffset>) -> Result<DateTime<FixedOffset>, ParseError> {
    let (year, month, day, hour, minute, second, tz) = try!(grammar::datetime(input));

    let year = year.unwrap_or(time.year());
    let month = month.unwrap_or(time.month());
    let day = day.unwrap_or(time.day());

    //set timezone
    let tz = tz.unwrap_or(time.offset().utc_minus_local());
    let tzo = FixedOffset::west(tz);
    let time = tzo.ymd(year, month, day);

    // set the time
    let time = time.and_hms(0,0,0);
    let time = try!(time.with_hour(hour).ok_or(ParseError::Hour));
    let time = try!(time.with_minute(minute).ok_or(ParseError::Minute));
    let time = try!(time.with_second(second as u32).ok_or(ParseError::Second));
    let nanosecond = second.fract() * 1_000_000_000.0;
    let time = try!(time.with_nanosecond(nanosecond as u32).ok_or(ParseError::Nanosecond));
    Ok(time)
}

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

    #[test]
    fn parse_rounding_test() {
        assert_eq!(parse_rounding("+15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("U15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("u15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("7.5").unwrap(), Rounding::Up(450.0));

        assert_eq!(parse_rounding("-30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("D30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("d7.5m").unwrap(), Rounding::Down(450.0));

        assert_eq!(parse_rounding("=1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("H1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("h7.5m").unwrap(), Rounding::Half(450.0));

        assert_eq!(parse_rounding("+30S").unwrap(), Rounding::Up(30.0));
        assert_eq!(parse_rounding("D30s").unwrap(), Rounding::Down(30.0));
        assert_eq!(parse_rounding("h30s").unwrap(), Rounding::Half(30.0));
        assert_eq!(parse_rounding("H1d").unwrap(), Rounding::Half(86400.0));
    }

    #[test]
    fn offset_test() {
        assert_eq!(grammar::offset("+2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("-1.55h").unwrap(), -5580.0);
        assert_eq!(grammar::offset("+2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("_2:22").unwrap(), -8520.0);
    }

    #[test]
    fn parse_offset_test() {
        let ctime = now();
        let n = ctime.checked_add_signed(Duration::seconds(5580)).unwrap();
        assert_eq!(parse_offset("+1.55h", ctime).unwrap(), n);
    }

    #[test]
    fn time_test() {
        assert_eq!(grammar::time("09:22:32").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32.055").unwrap(), (9, 22, 32.055));
        assert_eq!(grammar::time("9:22:32 Am").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22:32.055Pm").unwrap(), (21, 22, 32.055));
        assert_eq!(grammar::time("9:22 Pm").unwrap(), (21, 22, 0.0));
        // Might want to fail on these someday
        assert_eq!(grammar::time("9:22:32. Am").unwrap(), (9, 22, 32.0));
        assert_eq!(grammar::time("9:22: Pm").unwrap(), (21, 22, 0.0));
        assert_eq!(grammar::time("99:22:32").unwrap(), (99, 22, 32.0));
    }

    #[test]
    fn parse_time_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_time("9:22:31.9617 Pm", ctime).unwrap().to_rfc3339(), "2017-04-30T21:22:31.961700416-05:00");
        assert_eq!(parse_time("9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
    }

    #[test]
    fn datetime_test() {
        assert_eq!(grammar::datetime("4/2 9:22 Pm").unwrap(), (None, Some(4), Some(2), 21, 22, 0.0, None));
        assert_eq!(grammar::datetime("2017-4/2 9:22 Pm").unwrap(), (Some(2017), Some(4), Some(2), 21, 22, 0.0, None));
        assert_eq!(grammar::datetime("2017-4/2").unwrap(), (Some(2017), Some(4), Some(2), 0, 0, 0.0, None));
        assert_eq!(grammar::datetime("9:22 Pm").unwrap(), (None, None, None, 21, 22, 0.0, None));
    }

    #[test]
    fn parse_datetime_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_datetime("9:22:31.9617 Pm", ctime).unwrap().to_rfc3339(), "2017-04-30T21:22:31.961700416-05:00");
        assert_eq!(parse_datetime("9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
        assert_eq!(parse_datetime("2017-4-30 9:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
        assert_eq!(parse_datetime("2017-4-30", ctime).unwrap().to_rfc3339(), "2017-04-30T00:00:00-05:00");
    }

    #[test]
    #[should_panic(expected = "ParseError")]
    fn parse_datetime_seperator_test() {
        let ctime: DateTime<FixedOffset> = "2017-04-30T15:55:31.961764802-05:00".parse().unwrap();
        assert_eq!(parse_datetime("2017-4-309:22", ctime).unwrap().to_rfc3339(), "2017-04-30T09:22:00-05:00");
    }
}
<|endoftext|>


<commit_before>use parser::Error;

use self::Resolver::*;

#[derive(Clone, Debug)]
pub enum Resolver {
    /// A boolean expression
    /// Use Ast::parse to get an Ast
    Expr(Ast),
    /// A function
    Function(fn(u64) -> usize),
}

/// Finds the index of a pattern, outside of parenthesis
fn index_of<'a>(src: &'a str, pat: &'static str) -> Option<usize> {
    src.chars()
        .fold(
            (None, 0, 0, 0),
            |(match_index, i, n_matches, paren_level), ch| {
                if let Some(x) = match_index {
                    return (Some(x), i, n_matches, paren_level);
                } else {
                    let new_par_lvl = match ch {
                        '(' => paren_level + 1,
                        ')' => paren_level - 1,
                        _ => paren_level,
                    };

                    if Some(ch) == pat.chars().nth(n_matches) {
                        let length = n_matches + 1;
                        if length == pat.len() && new_par_lvl == 0 {
                            (Some(i - n_matches), i + 1, length, new_par_lvl)
                        } else {
                            (match_index, i + 1, length, new_par_lvl)
                        }
                    } else {
                        (match_index, i + 1, 0, new_par_lvl)
                    }
                }
            },
        ).0
}

use self::Ast::*;
#[derive(Clone, Debug, PartialEq)]
pub enum Ast {
    /// A ternary expression
    /// x ? a : b
    ///
    /// the three Ast<'a> are respectively x, a and b.
    Ternary(Box<Ast>, Box<Ast>, Box<Ast>),
    /// The n variable.
    N,
    /// Integer literals.
    Integer(u64),
    /// Binary operators.
    Op(Operator, Box<Ast>, Box<Ast>),
    /// ! operator.
    Not(Box<Ast>),
}

#[derive(Clone, Debug, PartialEq)]
pub enum Operator {
    Equal,
    NotEqual,
    GreaterOrEqual,
    SmallerOrEqual,
    Greater,
    Smaller,
    And,
    Or,
    Modulo,
}

impl Ast {
    fn resolve(&self, n: u64) -> usize {
        match *self {
            Ternary(ref cond, ref ok, ref nok) => if cond.resolve(n) == 0 {
                nok.resolve(n)
            } else {
                ok.resolve(n)
            },
            N => n as usize,
            Integer(x) => x as usize,
            Op(ref op, ref lhs, ref rhs) => match *op {
                Operator::Equal => (lhs.resolve(n) == rhs.resolve(n)) as usize,
                Operator::NotEqual => (lhs.resolve(n) != rhs.resolve(n)) as usize,
                Operator::GreaterOrEqual => (lhs.resolve(n) >= rhs.resolve(n)) as usize,
                Operator::SmallerOrEqual => (lhs.resolve(n) <= rhs.resolve(n)) as usize,
                Operator::Greater => (lhs.resolve(n) > rhs.resolve(n)) as usize,
                Operator::Smaller => (lhs.resolve(n) < rhs.resolve(n)) as usize,
                Operator::And => (lhs.resolve(n) != 0 && rhs.resolve(n) != 0) as usize,
                Operator::Or => (lhs.resolve(n) != 0 || rhs.resolve(n) != 0) as usize,
                Operator::Modulo => lhs.resolve(n) % rhs.resolve(n),
            },
            Not(ref val) => match val.resolve(n) {
                0 => 1,
                _ => 0,
            },
        }
    }

    pub fn parse<'a>(src: &'a str) -> Result<Ast, Error> {
        Self::parse_parens(src.trim())
    }

    fn parse_parens<'a>(src: &'a str) -> Result<Ast, Error> {
        if src.starts_with('(') && src.ends_with(')') {
            Ast::parse(src[1..src.len() - 1].trim())
        } else {
            Ast::parse_and(src.trim())
        }
    }

    fn parse_and<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "&&") {
            Ok(Ast::Op(
                Operator::And,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_or(src)
        }
    }

    fn parse_or<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "||") {
            Ok(Ast::Op(
                Operator::Or,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_ternary(src)
        }
    }

    fn parse_ternary<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "?") {
            if let Some(l) = index_of(src, ":") {
                Ok(Ast::Ternary(
                    Box::new(Ast::parse(&src[0..i])?),
                    Box::new(Ast::parse(&src[i + 1..l])?),
                    Box::new(Ast::parse(&src[l + 1..])?),
                ))
            } else {
                Err(Error::PluralParsing)
            }
        } else {
            Self::parse_ge(src)
        }
    }

    fn parse_ge<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, ">=") {
            Ok(Ast::Op(
                Operator::GreaterOrEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_gt(src)
        }
    }

    fn parse_gt<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, ">") {
            Ok(Ast::Op(
                Operator::Greater,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_le(src)
        }
    }

    fn parse_le<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "<=") {
            Ok(Ast::Op(
                Operator::SmallerOrEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_lt(src)
        }
    }

    fn parse_lt<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "<") {
            Ok(Ast::Op(
                Operator::Smaller,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_eq(src)
        }
    }

    fn parse_eq<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "==") {
            Ok(Ast::Op(
                Operator::Equal,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_neq(src)
        }
    }

    fn parse_neq<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "!=") {
            Ok(Ast::Op(
                Operator::NotEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_mod(src)
        }
    }
    fn parse_mod<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "%") {
            Ok(Ast::Op(
                Operator::Modulo,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_not(src.trim())
        }
    }

    fn parse_not<'a>(src: &'a str) -> Result<Ast, Error> {
        if index_of(src, "!") == Some(0) {
            Ok(Ast::Not(Box::new(Ast::parse(&src[1..])?)))
        } else {
            Self::parse_int(src.trim())
        }
    }

    fn parse_int<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Ok(x) = u64::from_str_radix(src, 10) {
            Ok(Ast::Integer(x))
        } else {
            Self::parse_n(src.trim())
        }
    }

    fn parse_n<'a>(src: &'a str) -> Result<Ast, Error> {
        if src == "n" {
            Ok(Ast::N)
        } else {
            Err(Error::PluralParsing)
        }
    }
}

impl Resolver {
    /// Returns the number of the correct plural form
    /// for `n` objects, as defined by the rule contained in this resolver.
    pub fn resolve(&self, n: u64) -> usize {
        match *self {
            Expr(ref ast) => ast.resolve(n),
            Function(ref f) => f(n),
        }
    }
}

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

    #[test]
    fn test_expr_resolver() {
        assert_eq!(Expr(N).resolve(42), 42);
    }

    #[test]
    fn test_parser() {
        assert_eq!(
            Ast::parse("n == 42 ? n : 6 && n < 7").expect("Invalid plural"),
            Ast::Op(
                Operator::And,
                Box::new(Ast::Ternary(
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(42))
                    )),
                    Box::new(Ast::N),
                    Box::new(Ast::Integer(6))
                )),
                Box::new(Ast::Op(
                    Operator::Smaller,
                    Box::new(Ast::N),
                    Box::new(Ast::Integer(7))
                ))
            )
        );

        assert_eq!(Ast::parse("(n)").expect("Invalid plural"), Ast::N);

        assert_eq!(
            Ast::parse("(n == 1 || n == 2) ? 0 : 1").expect("Invalid plural"),
            Ast::Ternary(
                Box::new(Ast::Op(
                    Operator::Or,
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(1))
                    )),
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(2))
                    ))
                )),
                Box::new(Ast::Integer(0)),
                Box::new(Ast::Integer(1))
            )
        )
    }
}
<commit_msg>Fix complex plural parsing<commit_after>use parser::Error;

use self::Resolver::*;

#[derive(Clone, Debug)]
pub enum Resolver {
    /// A boolean expression
    /// Use Ast::parse to get an Ast
    Expr(Ast),
    /// A function
    Function(fn(u64) -> usize),
}

/// Finds the index of a pattern, outside of parenthesis
fn index_of<'a>(src: &'a str, pat: &'static str) -> Option<usize> {
    src.chars()
        .fold(
            (None, 0, 0, 0),
            |(match_index, i, n_matches, paren_level), ch| {
                if let Some(x) = match_index {
                    return (Some(x), i, n_matches, paren_level);
                } else {
                    let new_par_lvl = match ch {
                        '(' => paren_level + 1,
                        ')' => paren_level - 1,
                        _ => paren_level,
                    };

                    if Some(ch) == pat.chars().nth(n_matches) {
                        let length = n_matches + 1;
                        if length == pat.len() && new_par_lvl == 0 {
                            (Some(i - n_matches), i + 1, length, new_par_lvl)
                        } else {
                            (match_index, i + 1, length, new_par_lvl)
                        }
                    } else {
                        (match_index, i + 1, 0, new_par_lvl)
                    }
                }
            },
        )
        .0
}

use self::Ast::*;
#[derive(Clone, Debug, PartialEq)]
pub enum Ast {
    /// A ternary expression
    /// x ? a : b
    ///
    /// the three Ast<'a> are respectively x, a and b.
    Ternary(Box<Ast>, Box<Ast>, Box<Ast>),
    /// The n variable.
    N,
    /// Integer literals.
    Integer(u64),
    /// Binary operators.
    Op(Operator, Box<Ast>, Box<Ast>),
    /// ! operator.
    Not(Box<Ast>),
}

#[derive(Clone, Debug, PartialEq)]
pub enum Operator {
    Equal,
    NotEqual,
    GreaterOrEqual,
    SmallerOrEqual,
    Greater,
    Smaller,
    And,
    Or,
    Modulo,
}

impl Ast {
    fn resolve(&self, n: u64) -> usize {
        match *self {
            Ternary(ref cond, ref ok, ref nok) => {
                if cond.resolve(n) == 0 {
                    nok.resolve(n)
                } else {
                    ok.resolve(n)
                }
            }
            N => n as usize,
            Integer(x) => x as usize,
            Op(ref op, ref lhs, ref rhs) => match *op {
                Operator::Equal => (lhs.resolve(n) == rhs.resolve(n)) as usize,
                Operator::NotEqual => (lhs.resolve(n) != rhs.resolve(n)) as usize,
                Operator::GreaterOrEqual => (lhs.resolve(n) >= rhs.resolve(n)) as usize,
                Operator::SmallerOrEqual => (lhs.resolve(n) <= rhs.resolve(n)) as usize,
                Operator::Greater => (lhs.resolve(n) > rhs.resolve(n)) as usize,
                Operator::Smaller => (lhs.resolve(n) < rhs.resolve(n)) as usize,
                Operator::And => (lhs.resolve(n) != 0 && rhs.resolve(n) != 0) as usize,
                Operator::Or => (lhs.resolve(n) != 0 || rhs.resolve(n) != 0) as usize,
                Operator::Modulo => lhs.resolve(n) % rhs.resolve(n),
            },
            Not(ref val) => match val.resolve(n) {
                0 => 1,
                _ => 0,
            },
        }
    }

    pub fn parse<'a>(src: &'a str) -> Result<Ast, Error> {
        Self::parse_parens(src.trim())
    }

    fn parse_parens<'a>(src: &'a str) -> Result<Ast, Error> {
        if src.starts_with('(') {
            let end = src[1..src.len() - 1].chars().fold((1, 2), |(level, index), ch| {
                if level > 0 {
                    if ch == ')' {
                        (level - 1, index + 1)
                    } else if ch == '(' {
                        (level + 1, index + 1)
                    } else {
                        (level, index + 1)
                    }
                } else {
                    if ch == '(' {
                        (level + 1, index + 1)
                    } else {
                        (level, index)
                    }
                }
            }).1;
            if end == src.len() {
                Ast::parse(src[1..src.len() - 1].trim())
            } else {
                Ast::parse_and(src.trim())
            }
        } else {
            Ast::parse_and(src.trim())
        }
    }

    fn parse_and<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "&&") {
            Ok(Ast::Op(
                Operator::And,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_or(src)
        }
    }

    fn parse_or<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "||") {
            Ok(Ast::Op(
                Operator::Or,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_ternary(src)
        }
    }

    fn parse_ternary<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "?") {
            if let Some(l) = index_of(src, ":") {
                Ok(Ast::Ternary(
                    Box::new(Ast::parse(&src[0..i])?),
                    Box::new(Ast::parse(&src[i + 1..l])?),
                    Box::new(Ast::parse(&src[l + 1..])?),
                ))
            } else {
                Err(Error::PluralParsing)
            }
        } else {
            Self::parse_ge(src)
        }
    }

    fn parse_ge<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, ">=") {
            Ok(Ast::Op(
                Operator::GreaterOrEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_gt(src)
        }
    }

    fn parse_gt<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, ">") {
            Ok(Ast::Op(
                Operator::Greater,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_le(src)
        }
    }

    fn parse_le<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "<=") {
            Ok(Ast::Op(
                Operator::SmallerOrEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_lt(src)
        }
    }

    fn parse_lt<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "<") {
            Ok(Ast::Op(
                Operator::Smaller,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_eq(src)
        }
    }

    fn parse_eq<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "==") {
            Ok(Ast::Op(
                Operator::Equal,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_neq(src)
        }
    }

    fn parse_neq<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "!=") {
            Ok(Ast::Op(
                Operator::NotEqual,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 2..])?),
            ))
        } else {
            Self::parse_mod(src)
        }
    }
    fn parse_mod<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Some(i) = index_of(src, "%") {
            Ok(Ast::Op(
                Operator::Modulo,
                Box::new(Ast::parse(&src[0..i])?),
                Box::new(Ast::parse(&src[i + 1..])?),
            ))
        } else {
            Self::parse_not(src.trim())
        }
    }

    fn parse_not<'a>(src: &'a str) -> Result<Ast, Error> {
        if index_of(src, "!") == Some(0) {
            Ok(Ast::Not(Box::new(Ast::parse(&src[1..])?)))
        } else {
            Self::parse_int(src.trim())
        }
    }

    fn parse_int<'a>(src: &'a str) -> Result<Ast, Error> {
        if let Ok(x) = u64::from_str_radix(src, 10) {
            Ok(Ast::Integer(x))
        } else {
            Self::parse_n(src.trim())
        }
    }

    fn parse_n<'a>(src: &'a str) -> Result<Ast, Error> {
        if src == "n" {
            Ok(Ast::N)
        } else {
            Err(Error::PluralParsing)
        }
    }
}

impl Resolver {
    /// Returns the number of the correct plural form
    /// for `n` objects, as defined by the rule contained in this resolver.
    pub fn resolve(&self, n: u64) -> usize {
        match *self {
            Expr(ref ast) => ast.resolve(n),
            Function(ref f) => f(n),
        }
    }
}

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

    #[test]
    fn test_expr_resolver() {
        assert_eq!(Expr(N).resolve(42), 42);
    }

    #[test]
    fn test_parser() {
        assert_eq!(
            Ast::parse("n == 42 ? n : 6 && n < 7").expect("Invalid plural"),
            Ast::Op(
                Operator::And,
                Box::new(Ast::Ternary(
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(42))
                    )),
                    Box::new(Ast::N),
                    Box::new(Ast::Integer(6))
                )),
                Box::new(Ast::Op(
                    Operator::Smaller,
                    Box::new(Ast::N),
                    Box::new(Ast::Integer(7))
                ))
            )
        );

        assert_eq!(Ast::parse("(n)").expect("Invalid plural"), Ast::N);

        assert_eq!(
            Ast::parse("(n == 1 || n == 2) ? 0 : 1").expect("Invalid plural"),
            Ast::Ternary(
                Box::new(Ast::Op(
                    Operator::Or,
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(1))
                    )),
                    Box::new(Ast::Op(
                        Operator::Equal,
                        Box::new(Ast::N),
                        Box::new(Ast::Integer(2))
                    ))
                )),
                Box::new(Ast::Integer(0)),
                Box::new(Ast::Integer(1))
            )
        );

        let ru_plural = "((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3))";
        assert!(Ast::parse(ru_plural).is_ok());
    }
}
<|endoftext|>