<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

//! Lints in the Rust compiler.
//!
//! This currently only contains the definitions and implementations
//! of most of the lints that `rustc` supports directly, it does not
//! contain the infrastructure for defining/registering lints. That is
//! available in `rustc::lint` and `rustc_plugin` respectively.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]

#![cfg_attr(test, feature(test))]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(macro_vis_matcher)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]

extern crate syntax;
#[macro_use]
extern crate rustc;
#[macro_use]
extern crate log;
extern crate rustc_mir;
extern crate rustc_target;
extern crate syntax_pos;

use rustc::lint;
use rustc::lint::builtin::{BARE_TRAIT_OBJECT, ABSOLUTE_PATH_STARTING_WITH_MODULE};
use rustc::session;
use rustc::util;

use session::Session;
use syntax::edition::Edition;
use lint::LintId;
use lint::FutureIncompatibleInfo;

mod bad_style;
mod builtin;
mod types;
mod unused;

use bad_style::*;
use builtin::*;
use types::*;
use unused::*;

/// Tell the `LintStore` about all the built-in lints (the ones
/// defined in this crate and the ones defined in
/// `rustc::lint::builtin`).
pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
    macro_rules! add_builtin {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_late_pass($sess, false, box $name);
                )*}
            )
    }

    macro_rules! add_early_builtin {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_early_pass($sess, false, box $name);
                )*}
            )
    }

    macro_rules! add_builtin_with_new {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_late_pass($sess, false, box $name::new());
                )*}
            )
    }

    macro_rules! add_early_builtin_with_new {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_early_pass($sess, false, box $name::new());
                )*}
            )
    }

    macro_rules! add_lint_group {
        ($sess:ident, $name:expr, $($lint:ident),*) => (
            store.register_group($sess, false, $name, vec![$(LintId::of($lint)),*]);
            )
    }

    add_early_builtin!(sess,
                       UnusedParens,
                       UnusedImportBraces,
                       AnonymousParameters,
                       UnusedDocComment,
                       );

    add_early_builtin_with_new!(sess,
                                DeprecatedAttr,
                                );

    add_builtin!(sess,
                 HardwiredLints,
                 WhileTrue,
                 ImproperCTypes,
                 VariantSizeDifferences,
                 BoxPointers,
                 UnusedAttributes,
                 PathStatements,
                 UnusedResults,
                 NonCamelCaseTypes,
                 NonSnakeCase,
                 NonUpperCaseGlobals,
                 NonShorthandFieldPatterns,
                 UnsafeCode,
                 UnusedAllocation,
                 MissingCopyImplementations,
                 UnstableFeatures,
                 UnconditionalRecursion,
                 InvalidNoMangleItems,
                 PluginAsLibrary,
                 MutableTransmutes,
                 UnionsWithDropFields,
                 UnreachablePub,
                 TypeAliasBounds,
                 UnusedBrokenConst,
                 );

    add_builtin_with_new!(sess,
                          TypeLimits,
                          MissingDoc,
                          MissingDebugImplementations,
                          );

    add_lint_group!(sess,
                    "bad_style",
                    NON_CAMEL_CASE_TYPES,
                    NON_SNAKE_CASE,
                    NON_UPPER_CASE_GLOBALS);

    add_lint_group!(sess,
                    "nonstandard_style",
                    NON_CAMEL_CASE_TYPES,
                    NON_SNAKE_CASE,
                    NON_UPPER_CASE_GLOBALS);

    add_lint_group!(sess,
                    "unused",
                    UNUSED_IMPORTS,
                    UNUSED_VARIABLES,
                    UNUSED_ASSIGNMENTS,
                    DEAD_CODE,
                    UNUSED_MUT,
                    UNREACHABLE_CODE,
                    UNREACHABLE_PATTERNS,
                    UNUSED_MUST_USE,
                    UNUSED_UNSAFE,
                    PATH_STATEMENTS,
                    UNUSED_ATTRIBUTES,
                    UNUSED_MACROS,
                    UNUSED_ALLOCATION,
                    UNUSED_DOC_COMMENT,
                    UNUSED_EXTERN_CRATES,
                    UNUSED_FEATURES,
                    UNUSED_PARENS);

    add_lint_group!(sess,
                    "rust_2018_idioms",
                    BARE_TRAIT_OBJECT,
                    UNREACHABLE_PUB);

    // Guidelines for creating a future incompatibility lint:
    //
    // - Create a lint defaulting to warn as normal, with ideally the same error
    //   message you would normally give
    // - Add a suitable reference, typically an RFC or tracking issue. Go ahead
    //   and include the full URL, sort items in ascending order of issue numbers.
    // - Later, change lint to error
    // - Eventually, remove lint
    store.register_future_incompatible(sess,
                                       vec![
        FutureIncompatibleInfo {
            id: LintId::of(PRIVATE_IN_PUBLIC),
            reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE),
            reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PATTERNS_IN_FNS_WITHOUT_BODY),
            reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(SAFE_EXTERN_STATICS),
            reference: "issue #36247 <https://github.com/rust-lang/rust/issues/36247>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(INVALID_TYPE_PARAM_DEFAULT),
            reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP),
            reference: "issue #37872 <https://github.com/rust-lang/rust/issues/37872>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_IMPORTS),
            reference: "issue #38260 <https://github.com/rust-lang/rust/issues/38260>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY),
            reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(MISSING_FRAGMENT_SPECIFIER),
            reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN),
            reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(ANONYMOUS_PARAMETERS),
            reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES),
            reference: "issue #42238 <https://github.com/rust-lang/rust/issues/42238>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS),
            reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(SAFE_PACKED_BORROWS),
            reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(INCOHERENT_FUNDAMENTAL_IMPLS),
            reference: "issue #46205 <https://github.com/rust-lang/rust/issues/46205>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
            reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
            edition: Some(Edition::Edition2018),
        },
        FutureIncompatibleInfo {
            id: LintId::of(UNSTABLE_NAME_COLLISION),
            reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
            edition: None,
            // Note: this item represents future incompatibility of all unstable functions in the
            //       standard library, and thus should never be removed or changed to an error.
        },
        FutureIncompatibleInfo {
            id: LintId::of(ABSOLUTE_PATH_STARTING_WITH_MODULE),
            reference: "issue TBD",
            edition: Some(Edition::Edition2018),
        },
        ]);

    // Register renamed and removed lints
    store.register_renamed("unknown_features", "unused_features");
    store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
    store.register_removed("negate_unsigned", "cast a signed value instead");
    store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok");
    // This was renamed to raw_pointer_derive, which was then removed,
    // so it is also considered removed
    store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok");
    store.register_removed("drop_with_repr_extern", "drop flags have been removed");
    store.register_removed("fat_ptr_transmutes", "was accidentally removed back in 2014");
    store.register_removed("deprecated_attr", "use `deprecated` instead");
    store.register_removed("transmute_from_fn_item_types",
        "always cast functions before transmuting them");
    store.register_removed("hr_lifetime_in_assoc_type",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/33685");
    store.register_removed("inaccessible_extern_crate",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36886");
    store.register_removed("super_or_self_in_global_path",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36888");
    store.register_removed("overlapping_inherent_impls",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36889");
    store.register_removed("illegal_floating_point_constant_pattern",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36890");
    store.register_removed("illegal_struct_or_enum_constant_pattern",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36891");
    store.register_removed("lifetime_underscore",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36892");
    store.register_removed("extra_requirement_in_impl",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/37166");
    store.register_removed("coerce_never",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
    store.register_removed("resolve_trait_on_defaulted_unit",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
}
<commit_msg>Rename idiom lints to migration lints<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

//! Lints in the Rust compiler.
//!
//! This currently only contains the definitions and implementations
//! of most of the lints that `rustc` supports directly, it does not
//! contain the infrastructure for defining/registering lints. That is
//! available in `rustc::lint` and `rustc_plugin` respectively.
//!
//! # Note
//!
//! This API is completely unstable and subject to change.

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
      html_root_url = "https://doc.rust-lang.org/nightly/")]

#![cfg_attr(test, feature(test))]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(macro_vis_matcher)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]

extern crate syntax;
#[macro_use]
extern crate rustc;
#[macro_use]
extern crate log;
extern crate rustc_mir;
extern crate rustc_target;
extern crate syntax_pos;

use rustc::lint;
use rustc::lint::builtin::{BARE_TRAIT_OBJECT, ABSOLUTE_PATH_STARTING_WITH_MODULE};
use rustc::session;
use rustc::util;

use session::Session;
use syntax::edition::Edition;
use lint::LintId;
use lint::FutureIncompatibleInfo;

mod bad_style;
mod builtin;
mod types;
mod unused;

use bad_style::*;
use builtin::*;
use types::*;
use unused::*;

/// Tell the `LintStore` about all the built-in lints (the ones
/// defined in this crate and the ones defined in
/// `rustc::lint::builtin`).
pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
    macro_rules! add_builtin {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_late_pass($sess, false, box $name);
                )*}
            )
    }

    macro_rules! add_early_builtin {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_early_pass($sess, false, box $name);
                )*}
            )
    }

    macro_rules! add_builtin_with_new {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_late_pass($sess, false, box $name::new());
                )*}
            )
    }

    macro_rules! add_early_builtin_with_new {
        ($sess:ident, $($name:ident),*,) => (
            {$(
                store.register_early_pass($sess, false, box $name::new());
                )*}
            )
    }

    macro_rules! add_lint_group {
        ($sess:ident, $name:expr, $($lint:ident),*) => (
            store.register_group($sess, false, $name, vec![$(LintId::of($lint)),*]);
            )
    }

    add_early_builtin!(sess,
                       UnusedParens,
                       UnusedImportBraces,
                       AnonymousParameters,
                       UnusedDocComment,
                       );

    add_early_builtin_with_new!(sess,
                                DeprecatedAttr,
                                );

    add_builtin!(sess,
                 HardwiredLints,
                 WhileTrue,
                 ImproperCTypes,
                 VariantSizeDifferences,
                 BoxPointers,
                 UnusedAttributes,
                 PathStatements,
                 UnusedResults,
                 NonCamelCaseTypes,
                 NonSnakeCase,
                 NonUpperCaseGlobals,
                 NonShorthandFieldPatterns,
                 UnsafeCode,
                 UnusedAllocation,
                 MissingCopyImplementations,
                 UnstableFeatures,
                 UnconditionalRecursion,
                 InvalidNoMangleItems,
                 PluginAsLibrary,
                 MutableTransmutes,
                 UnionsWithDropFields,
                 UnreachablePub,
                 TypeAliasBounds,
                 UnusedBrokenConst,
                 );

    add_builtin_with_new!(sess,
                          TypeLimits,
                          MissingDoc,
                          MissingDebugImplementations,
                          );

    add_lint_group!(sess,
                    "bad_style",
                    NON_CAMEL_CASE_TYPES,
                    NON_SNAKE_CASE,
                    NON_UPPER_CASE_GLOBALS);

    add_lint_group!(sess,
                    "nonstandard_style",
                    NON_CAMEL_CASE_TYPES,
                    NON_SNAKE_CASE,
                    NON_UPPER_CASE_GLOBALS);

    add_lint_group!(sess,
                    "unused",
                    UNUSED_IMPORTS,
                    UNUSED_VARIABLES,
                    UNUSED_ASSIGNMENTS,
                    DEAD_CODE,
                    UNUSED_MUT,
                    UNREACHABLE_CODE,
                    UNREACHABLE_PATTERNS,
                    UNUSED_MUST_USE,
                    UNUSED_UNSAFE,
                    PATH_STATEMENTS,
                    UNUSED_ATTRIBUTES,
                    UNUSED_MACROS,
                    UNUSED_ALLOCATION,
                    UNUSED_DOC_COMMENT,
                    UNUSED_EXTERN_CRATES,
                    UNUSED_FEATURES,
                    UNUSED_PARENS);

    add_lint_group!(sess,
                    "rust_2018_migration",
                    BARE_TRAIT_OBJECT,
                    UNREACHABLE_PUB);

    // Guidelines for creating a future incompatibility lint:
    //
    // - Create a lint defaulting to warn as normal, with ideally the same error
    //   message you would normally give
    // - Add a suitable reference, typically an RFC or tracking issue. Go ahead
    //   and include the full URL, sort items in ascending order of issue numbers.
    // - Later, change lint to error
    // - Eventually, remove lint
    store.register_future_incompatible(sess,
                                       vec![
        FutureIncompatibleInfo {
            id: LintId::of(PRIVATE_IN_PUBLIC),
            reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE),
            reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PATTERNS_IN_FNS_WITHOUT_BODY),
            reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(SAFE_EXTERN_STATICS),
            reference: "issue #36247 <https://github.com/rust-lang/rust/issues/36247>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(INVALID_TYPE_PARAM_DEFAULT),
            reference: "issue #36887 <https://github.com/rust-lang/rust/issues/36887>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP),
            reference: "issue #37872 <https://github.com/rust-lang/rust/issues/37872>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_IMPORTS),
            reference: "issue #38260 <https://github.com/rust-lang/rust/issues/38260>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY),
            reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(MISSING_FRAGMENT_SPECIFIER),
            reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(ILLEGAL_FLOATING_POINT_LITERAL_PATTERN),
            reference: "issue #41620 <https://github.com/rust-lang/rust/issues/41620>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(ANONYMOUS_PARAMETERS),
            reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES),
            reference: "issue #42238 <https://github.com/rust-lang/rust/issues/42238>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(LATE_BOUND_LIFETIME_ARGUMENTS),
            reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(SAFE_PACKED_BORROWS),
            reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(INCOHERENT_FUNDAMENTAL_IMPLS),
            reference: "issue #46205 <https://github.com/rust-lang/rust/issues/46205>",
            edition: None,
        },
        FutureIncompatibleInfo {
            id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
            reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
            edition: Some(Edition::Edition2018),
        },
        FutureIncompatibleInfo {
            id: LintId::of(UNSTABLE_NAME_COLLISION),
            reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
            edition: None,
            // Note: this item represents future incompatibility of all unstable functions in the
            //       standard library, and thus should never be removed or changed to an error.
        },
        FutureIncompatibleInfo {
            id: LintId::of(ABSOLUTE_PATH_STARTING_WITH_MODULE),
            reference: "issue TBD",
            edition: Some(Edition::Edition2018),
        },
        ]);

    // Register renamed and removed lints
    store.register_renamed("unknown_features", "unused_features");
    store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
    store.register_removed("negate_unsigned", "cast a signed value instead");
    store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok");
    // This was renamed to raw_pointer_derive, which was then removed,
    // so it is also considered removed
    store.register_removed("raw_pointer_deriving", "using derive with raw pointers is ok");
    store.register_removed("drop_with_repr_extern", "drop flags have been removed");
    store.register_removed("fat_ptr_transmutes", "was accidentally removed back in 2014");
    store.register_removed("deprecated_attr", "use `deprecated` instead");
    store.register_removed("transmute_from_fn_item_types",
        "always cast functions before transmuting them");
    store.register_removed("hr_lifetime_in_assoc_type",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/33685");
    store.register_removed("inaccessible_extern_crate",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36886");
    store.register_removed("super_or_self_in_global_path",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36888");
    store.register_removed("overlapping_inherent_impls",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36889");
    store.register_removed("illegal_floating_point_constant_pattern",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36890");
    store.register_removed("illegal_struct_or_enum_constant_pattern",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36891");
    store.register_removed("lifetime_underscore",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/36892");
    store.register_removed("extra_requirement_in_impl",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/37166");
    store.register_removed("coerce_never",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
    store.register_removed("resolve_trait_on_defaulted_unit",
        "converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
}
<|endoftext|>


<commit_before>#[macro_use]
mod common;

use std::os::unix::raw::mode_t;
use std::os::unix::fs::PermissionsExt;
use std::os::unix::fs::OpenOptionsExt;
use common::util::*;

static UTIL_NAME: &'static str = "chmod";
static TEST_FILE: &'static str = "file";
static REFERENCE_FILE: &'static str = "reference";
static REFERENCE_PERMS: mode_t = 0o247;

struct TestCase {
    args: Vec<&'static str>,
    before: mode_t,
    after: mode_t
}

fn mkfile(file: &str, mode: mode_t) {
    std::fs::OpenOptions::new().mode(mode).create(true).write(true).open(file).unwrap();
    let mut perms = std::fs::metadata(file).unwrap().permissions();
    perms.set_mode(mode);
    std::fs::set_permissions(file, perms).unwrap();
}

fn run_tests(tests: Vec<TestCase>) {
    for test in tests {
        let (at, mut ucmd) = testing(UTIL_NAME);

        mkfile(&at.plus_as_string(TEST_FILE), test.before);
        mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
        let perms = at.metadata(TEST_FILE).permissions().mode();
        if perms != test.before{
            panic!(format!("{}: expected: {:o} got: {:o}", "setting permissions failed", test.after, perms));
        }

        for arg in test.args {
            ucmd.arg(arg);
        }
        let r = ucmd.run();
        if !r.success {
            println!("{}", r.stderr);
            panic!(format!("{:?}: failed", ucmd.raw));
        }

        let perms = at.metadata(TEST_FILE).permissions().mode();
        if perms != test.after {
            panic!(format!("{:?}: expected: {:o} got: {:o}", ucmd.raw, test.after, perms));
        }
    }
}

#[test]
fn test_chmod_octal() {
    let tests = vec!{
        TestCase{args: vec!{"0700",  TEST_FILE}, before: 0o000, after: 0o700},
        TestCase{args: vec!{"0070",  TEST_FILE}, before: 0o000, after: 0o070},
        TestCase{args: vec!{"0007",  TEST_FILE}, before: 0o000, after: 0o007},
        // Known failues: #788
        // TestCase{args: vec!{"-0700", TEST_FILE}, before: 0o700, after: 0o000},
        // TestCase{args: vec!{"-0070", TEST_FILE}, before: 0o060, after: 0o000},
        // TestCase{args: vec!{"-0007", TEST_FILE}, before: 0o001, after: 0o000},
        TestCase{args: vec!{"+0100", TEST_FILE}, before: 0o600, after: 0o700},
        TestCase{args: vec!{"+0020", TEST_FILE}, before: 0o050, after: 0o070},
        TestCase{args: vec!{"+0004", TEST_FILE}, before: 0o003, after: 0o007},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_ugoa() {
    let tests = vec!{
        TestCase{args: vec!{"u=rwx", TEST_FILE}, before: 0o000, after: 0o700},
        TestCase{args: vec!{"g=rwx", TEST_FILE}, before: 0o000, after: 0o070},
        TestCase{args: vec!{"o=rwx", TEST_FILE}, before: 0o000, after: 0o007},
        TestCase{args: vec!{"a=rwx", TEST_FILE}, before: 0o000, after: 0o777},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_ugo_copy() {
    let tests = vec!{
        TestCase{args: vec!{"u=g", TEST_FILE}, before: 0o070, after: 0o770},
        TestCase{args: vec!{"g=o", TEST_FILE}, before: 0o005, after: 0o055},
        TestCase{args: vec!{"o=u", TEST_FILE}, before: 0o200, after: 0o202},
        TestCase{args: vec!{"u-g", TEST_FILE}, before: 0o710, after: 0o610},
        TestCase{args: vec!{"u+g", TEST_FILE}, before: 0o250, after: 0o750},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_reference_file() {
    let tests = vec!{
        TestCase{args: vec!{"--reference", REFERENCE_FILE, TEST_FILE}, before: 0o070, after: 0o247},
    };
    run_tests(tests);
}
<commit_msg>tests/chmod: refactor tests to only create reference file once<commit_after>#[macro_use]
mod common;

use std::os::unix::raw::mode_t;
use std::os::unix::fs::PermissionsExt;
use std::os::unix::fs::OpenOptionsExt;
use common::util::*;

static UTIL_NAME: &'static str = "chmod";
static TEST_FILE: &'static str = "file";
static REFERENCE_FILE: &'static str = "reference";
static REFERENCE_PERMS: mode_t = 0o247;

struct TestCase {
    args: Vec<&'static str>,
    before: mode_t,
    after: mode_t
}

fn mkfile(file: &str, mode: mode_t) {
    std::fs::OpenOptions::new().mode(mode).create(true).write(true).open(file).unwrap();
    let mut perms = std::fs::metadata(file).unwrap().permissions();
    perms.set_mode(mode);
    std::fs::set_permissions(file, perms).unwrap();
}

fn run_single_test(test: &TestCase, at: AtPath, mut ucmd: UCommand) {
        mkfile(&at.plus_as_string(TEST_FILE), test.before);
        let perms = at.metadata(TEST_FILE).permissions().mode();
        if perms != test.before{
            panic!(format!("{}: expected: {:o} got: {:o}", "setting permissions failed", test.after, perms));
        }

        for arg in &test.args {
            ucmd.arg(arg);
        }
        let r = ucmd.run();
        if !r.success {
            println!("{}", r.stderr);
            panic!(format!("{:?}: failed", ucmd.raw));
        }

        let perms = at.metadata(TEST_FILE).permissions().mode();
        if perms != test.after {
            panic!(format!("{:?}: expected: {:o} got: {:o}", ucmd.raw, test.after, perms));
        }
}

fn run_tests(tests: Vec<TestCase>) {
    for test in tests {
        let (at, ucmd) = testing(UTIL_NAME);
        run_single_test(&test, at, ucmd);
    }
}

#[test]
fn test_chmod_octal() {
    let tests = vec!{
        TestCase{args: vec!{"0700",  TEST_FILE}, before: 0o000, after: 0o700},
        TestCase{args: vec!{"0070",  TEST_FILE}, before: 0o000, after: 0o070},
        TestCase{args: vec!{"0007",  TEST_FILE}, before: 0o000, after: 0o007},
        // Known failues: #788
        // TestCase{args: vec!{"-0700", TEST_FILE}, before: 0o700, after: 0o000},
        // TestCase{args: vec!{"-0070", TEST_FILE}, before: 0o060, after: 0o000},
        // TestCase{args: vec!{"-0007", TEST_FILE}, before: 0o001, after: 0o000},
        TestCase{args: vec!{"+0100", TEST_FILE}, before: 0o600, after: 0o700},
        TestCase{args: vec!{"+0020", TEST_FILE}, before: 0o050, after: 0o070},
        TestCase{args: vec!{"+0004", TEST_FILE}, before: 0o003, after: 0o007},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_ugoa() {
    let tests = vec!{
        TestCase{args: vec!{"u=rwx", TEST_FILE}, before: 0o000, after: 0o700},
        TestCase{args: vec!{"g=rwx", TEST_FILE}, before: 0o000, after: 0o070},
        TestCase{args: vec!{"o=rwx", TEST_FILE}, before: 0o000, after: 0o007},
        TestCase{args: vec!{"a=rwx", TEST_FILE}, before: 0o000, after: 0o777},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_ugo_copy() {
    let tests = vec!{
        TestCase{args: vec!{"u=g", TEST_FILE}, before: 0o070, after: 0o770},
        TestCase{args: vec!{"g=o", TEST_FILE}, before: 0o005, after: 0o055},
        TestCase{args: vec!{"o=u", TEST_FILE}, before: 0o200, after: 0o202},
        TestCase{args: vec!{"u-g", TEST_FILE}, before: 0o710, after: 0o610},
        TestCase{args: vec!{"u+g", TEST_FILE}, before: 0o250, after: 0o750},
    };
    run_tests(tests);
}

#[test]
fn test_chmod_reference_file() {
    let tests = vec!{
        TestCase{args: vec!{"--reference", REFERENCE_FILE, TEST_FILE}, before: 0o070, after: 0o247},
    };
    let (at, ucmd) = testing(UTIL_NAME);
    mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
    run_single_test(&tests[0], at, ucmd);
}
<|endoftext|>


<commit_before>extern crate memcache;

#[test]
fn test_raw() {
    let client = memcache::connect("localhost", 2333).unwrap();
    client.flush(0);

    assert!(!client.exist("foo").unwrap());

    client.set_raw("foo", &[0x1u8, 0x2u8, 0x3u8], 0, 42).unwrap();
    assert!(client.exist("foo").unwrap());
}
<commit_msg>update<commit_after>extern crate memcache;

#[test]
fn test_exist() {
    let client = memcache::connect("localhost", 2333).unwrap();
    client.flush(0);

    assert!(!client.exist("foo").unwrap());

    client.set_raw("foo", &[0x1u8, 0x2u8, 0x3u8], 0, 42).unwrap();
    assert!(client.exist("foo").unwrap());
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// 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::collections::{HashMap, HashSet};

use pest::Error;
use pest::inputs::{Input, Span};
use pest::iterators::Pairs;

use quote::Ident;

use super::parser::{GrammarRule, ParserExpr, ParserNode, ParserRule};

pub fn validate_pairs<I: Input>(pairs: Pairs<GrammarRule, I>) -> Vec<Ident> {
    let mut rust_keywords = HashSet::new();
    rust_keywords.insert("abstract");
    rust_keywords.insert("alignof");
    rust_keywords.insert("as");
    rust_keywords.insert("become");
    rust_keywords.insert("box");
    rust_keywords.insert("break");
    rust_keywords.insert("const");
    rust_keywords.insert("continue");
    rust_keywords.insert("crate");
    rust_keywords.insert("do");
    rust_keywords.insert("else");
    rust_keywords.insert("enum");
    rust_keywords.insert("extern");
    rust_keywords.insert("false");
    rust_keywords.insert("final");
    rust_keywords.insert("fn");
    rust_keywords.insert("for");
    rust_keywords.insert("if");
    rust_keywords.insert("impl");
    rust_keywords.insert("in");
    rust_keywords.insert("let");
    rust_keywords.insert("loop");
    rust_keywords.insert("macro");
    rust_keywords.insert("match");
    rust_keywords.insert("mod");
    rust_keywords.insert("move");
    rust_keywords.insert("mut");
    rust_keywords.insert("offsetof");
    rust_keywords.insert("override");
    rust_keywords.insert("priv");
    rust_keywords.insert("proc");
    rust_keywords.insert("pure");
    rust_keywords.insert("pub");
    rust_keywords.insert("ref");
    rust_keywords.insert("return");
    rust_keywords.insert("Self");
    rust_keywords.insert("self");
    rust_keywords.insert("sizeof");
    rust_keywords.insert("static");
    rust_keywords.insert("struct");
    rust_keywords.insert("super");
    rust_keywords.insert("trait");
    rust_keywords.insert("true");
    rust_keywords.insert("type");
    rust_keywords.insert("typeof");
    rust_keywords.insert("unsafe");
    rust_keywords.insert("unsized");
    rust_keywords.insert("use");
    rust_keywords.insert("virtual");
    rust_keywords.insert("where");
    rust_keywords.insert("while");
    rust_keywords.insert("yield");

    let mut pest_keywords = HashSet::new();
    pest_keywords.insert("any");
    pest_keywords.insert("eoi");
    pest_keywords.insert("peek");
    pest_keywords.insert("pop");
    pest_keywords.insert("push");
    pest_keywords.insert("soi");

    let mut predefined = HashSet::new();
    predefined.insert("any");
    predefined.insert("eoi");
    predefined.insert("peek");
    predefined.insert("pop");
    predefined.insert("soi");

    let definitions: Vec<_> = pairs.clone()
                                   .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                   .map(|pair| {
                                       pair.into_inner()
                                           .next()
                                           .unwrap()
                                           .into_span()
                                   }).collect();
    let called_rules: Vec<_> = pairs.clone()
                                    .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                    .flat_map(|pair| {
                                        pair.into_inner().flatten().skip(1).filter(|pair| {
                                            pair.as_rule() == GrammarRule::identifier
                                        }).map(|pair| {
                                            pair.into_span()
                                        })
                                    }).collect();

    let mut errors = vec![];

    errors.extend(validate_rust_keywords(&definitions, &rust_keywords));
    errors.extend(validate_pest_keywords(&definitions, &pest_keywords));
    errors.extend(validate_already_defined(&definitions));
    errors.extend(validate_undefined(&definitions, &called_rules, &predefined));

    let errors = errors.into_iter().map(|error| {
        format!("grammar error\n\n{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("{}", errors);
    }

    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();
    let called_rules: HashSet<_> = called_rules.iter().map(|span| span.as_str()).collect();

    let defaults = called_rules.difference(&definitions);

    defaults.into_iter().map(|string| Ident::new(*string)).collect()
}

pub fn validate_rust_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    rust_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if rust_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a rust keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_pest_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    pest_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if pest_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a pest keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_already_defined<I: Input>(
    definitions: &Vec<Span<I>>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let mut defined = HashSet::new();

    for definition in definitions {
        let name = definition.as_str();

        if defined.contains(&name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} already defined", name),
                span: definition.clone()
            })
        } else {
            defined.insert(name);
        }
    }

    errors
}

pub fn validate_undefined<I: Input>(
    definitions: &Vec<Span<I>>,
    called_rules: &Vec<Span<I>>,
    predefined: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();

    for rule in called_rules {
        let name = rule.as_str();

        if !definitions.contains(name) && !predefined.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} is undefined", name),
                span: rule.clone()
            })
        }
    }

    errors
}

pub fn validate_ast<I: Input>(rules: &Vec<ParserRule<I>>) {
    let mut errors = vec![];

    errors.extend(validate_left_recursion(rules));

    errors.sort_by_key(|error| {
        match *error {
            Error::CustomErrorSpan { ref span, .. } => span.clone(),
            _ => unreachable!()
        }
    });

    let errors = errors.into_iter().map(|error| {
        format!("{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("grammar error\n\n{}", errors);
    }
}

fn validate_left_recursion<I: Input>(rules: &Vec<ParserRule<I>>) -> Vec<Error<GrammarRule, I>> {
    left_recursion(to_hash_map(rules))
}

fn to_hash_map<I: Input>(rules: &Vec<ParserRule<I>>) -> HashMap<Ident, &ParserNode<I>> {
    let mut hash_map = HashMap::new();

    for rule in rules {
        hash_map.insert(rule.name.clone(), &rule.node);
    }

    hash_map
}

fn is_non_failing<I: Input>(expr: &ParserExpr<I>) -> bool {
    match *expr {
        ParserExpr::Str(ref string) => string == "",
        ParserExpr::Opt(_) => true,
        ParserExpr::Rep(_) => true,
        ParserExpr::Seq(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) && is_non_failing(&rhs.expr)
        }
        ParserExpr::Choice(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) || is_non_failing(&rhs.expr)
        }
        _ => false
    }
}

fn left_recursion<I: Input>(rules: HashMap<Ident, &ParserNode<I>>) -> Vec<Error<GrammarRule, I>> {
    fn check_expr<I: Input>(
        mut names: HashSet<Ident>,
        node: &ParserNode<I>,
        rules: &HashMap<Ident, &ParserNode<I>>
    ) -> Option<Error<GrammarRule, I>> {
        match node.expr {
            ParserExpr::Ident(ref other)  => {
                if names.contains(other) {
                    return Some(Error::CustomErrorSpan {
                        message: format!("rule {} is left-recursive; pest::prec_climber might be \
                                          useful in this case", node.span.as_str()),
                        span: node.span.clone()
                    });
                }

                names.insert(other.clone());

                if let Some(node) = rules.get(other) {
                    check_expr(names, node, rules)
                } else {
                    None
                }
            },
            ParserExpr::Seq(ref lhs, ref rhs) => {
                if is_non_failing(&lhs.expr) {
                    check_expr(names, rhs, rules)
                } else {
                    check_expr(names, lhs, rules)
                }
            }
            ParserExpr::Choice(ref lhs, _) => check_expr(names, &lhs, rules),
            ParserExpr::Rep(ref node) => check_expr(names, &node, rules),
            ParserExpr::RepOnce(ref node) => check_expr(names, &node, rules),
            ParserExpr::Opt(ref node) => check_expr(names, &node, rules),
            ParserExpr::PosPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::NegPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::Push(ref node) => check_expr(names, &node, rules),
            _ => None
        }
    }

    let mut errors = vec![];

    for (ref name, ref node) in &rules {
        let mut names = HashSet::new();

        names.insert((*name).clone());

        if let Some(error) = check_expr(names, node, &rules) {
            errors.push(error);
        }
    }

    errors
}

#[cfg(test)]
mod tests {
    use pest::Parser;

    use super::*;
    use super::super::parser::{GrammarParser, consume_rules};

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | let = { \"a\" }
  | ^-^
  |
  = let is a rust keyword")]
    fn rust_keyword() {
        let input = "let = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | any = { \"a\" }
  | ^-^
  |
  = any is a pest keyword")]
    fn pest_keyword() {
        let input = "any = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:13
  |
1 | a = { \"a\" } a = { \"a\" }
  |             ^
  |
  = rule a already defined")]
    fn already_defined() {
        let input = "a = { \"a\" } a = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b }
  |       ^
  |
  = rule b is undefined")]
    fn undefined() {
        let input = "a = { b }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { a }
  |       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn simple_left_recursion() {
        let input = "a = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b } b = { a }
  |       ^
  |
  = rule b is left-recursive; pest::prec_climber might be useful in this case

 --> 1:17
  |
1 | a = { b } b = { a }
  |                 ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn indirect_left_recursion() {
        let input = "a = { b } b = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:39
  |
1 | a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }
  |                                       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn non_failing_left_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    fn valid_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"b\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }
}
<commit_msg>Added validation for non-failing repetitions. Fixes #133.<commit_after>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// 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::collections::{HashMap, HashSet};

use pest::Error;
use pest::inputs::{Input, Span};
use pest::iterators::Pairs;

use quote::Ident;

use super::parser::{GrammarRule, ParserExpr, ParserNode, ParserRule};

pub fn validate_pairs<I: Input>(pairs: Pairs<GrammarRule, I>) -> Vec<Ident> {
    let mut rust_keywords = HashSet::new();
    rust_keywords.insert("abstract");
    rust_keywords.insert("alignof");
    rust_keywords.insert("as");
    rust_keywords.insert("become");
    rust_keywords.insert("box");
    rust_keywords.insert("break");
    rust_keywords.insert("const");
    rust_keywords.insert("continue");
    rust_keywords.insert("crate");
    rust_keywords.insert("do");
    rust_keywords.insert("else");
    rust_keywords.insert("enum");
    rust_keywords.insert("extern");
    rust_keywords.insert("false");
    rust_keywords.insert("final");
    rust_keywords.insert("fn");
    rust_keywords.insert("for");
    rust_keywords.insert("if");
    rust_keywords.insert("impl");
    rust_keywords.insert("in");
    rust_keywords.insert("let");
    rust_keywords.insert("loop");
    rust_keywords.insert("macro");
    rust_keywords.insert("match");
    rust_keywords.insert("mod");
    rust_keywords.insert("move");
    rust_keywords.insert("mut");
    rust_keywords.insert("offsetof");
    rust_keywords.insert("override");
    rust_keywords.insert("priv");
    rust_keywords.insert("proc");
    rust_keywords.insert("pure");
    rust_keywords.insert("pub");
    rust_keywords.insert("ref");
    rust_keywords.insert("return");
    rust_keywords.insert("Self");
    rust_keywords.insert("self");
    rust_keywords.insert("sizeof");
    rust_keywords.insert("static");
    rust_keywords.insert("struct");
    rust_keywords.insert("super");
    rust_keywords.insert("trait");
    rust_keywords.insert("true");
    rust_keywords.insert("type");
    rust_keywords.insert("typeof");
    rust_keywords.insert("unsafe");
    rust_keywords.insert("unsized");
    rust_keywords.insert("use");
    rust_keywords.insert("virtual");
    rust_keywords.insert("where");
    rust_keywords.insert("while");
    rust_keywords.insert("yield");

    let mut pest_keywords = HashSet::new();
    pest_keywords.insert("any");
    pest_keywords.insert("eoi");
    pest_keywords.insert("peek");
    pest_keywords.insert("pop");
    pest_keywords.insert("push");
    pest_keywords.insert("soi");

    let mut predefined = HashSet::new();
    predefined.insert("any");
    predefined.insert("eoi");
    predefined.insert("peek");
    predefined.insert("pop");
    predefined.insert("soi");

    let definitions: Vec<_> = pairs.clone()
                                   .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                   .map(|pair| {
                                       pair.into_inner()
                                           .next()
                                           .unwrap()
                                           .into_span()
                                   }).collect();
    let called_rules: Vec<_> = pairs.clone()
                                    .filter(|pair| pair.as_rule() == GrammarRule::grammar_rule)
                                    .flat_map(|pair| {
                                        pair.into_inner().flatten().skip(1).filter(|pair| {
                                            pair.as_rule() == GrammarRule::identifier
                                        }).map(|pair| {
                                            pair.into_span()
                                        })
                                    }).collect();

    let mut errors = vec![];

    errors.extend(validate_rust_keywords(&definitions, &rust_keywords));
    errors.extend(validate_pest_keywords(&definitions, &pest_keywords));
    errors.extend(validate_already_defined(&definitions));
    errors.extend(validate_undefined(&definitions, &called_rules, &predefined));

    let errors = errors.into_iter().map(|error| {
        format!("grammar error\n\n{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("{}", errors);
    }

    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();
    let called_rules: HashSet<_> = called_rules.iter().map(|span| span.as_str()).collect();

    let defaults = called_rules.difference(&definitions);

    defaults.into_iter().map(|string| Ident::new(*string)).collect()
}

pub fn validate_rust_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    rust_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if rust_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a rust keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_pest_keywords<I: Input>(
    definitions: &Vec<Span<I>>,
    pest_keywords: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];

    for definition in definitions {
        let name = definition.as_str();

        if pest_keywords.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("{} is a pest keyword", name),
                span: definition.clone()
            })
        }
    }

    errors
}

pub fn validate_already_defined<I: Input>(
    definitions: &Vec<Span<I>>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let mut defined = HashSet::new();

    for definition in definitions {
        let name = definition.as_str();

        if defined.contains(&name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} already defined", name),
                span: definition.clone()
            })
        } else {
            defined.insert(name);
        }
    }

    errors
}

pub fn validate_undefined<I: Input>(
    definitions: &Vec<Span<I>>,
    called_rules: &Vec<Span<I>>,
    predefined: &HashSet<&str>
) -> Vec<Error<GrammarRule, I>> {
    let mut errors = vec![];
    let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();

    for rule in called_rules {
        let name = rule.as_str();

        if !definitions.contains(name) && !predefined.contains(name) {
            errors.push(Error::CustomErrorSpan {
                message: format!("rule {} is undefined", name),
                span: rule.clone()
            })
        }
    }

    errors
}

pub fn validate_ast<I: Input>(rules: &Vec<ParserRule<I>>) {
    let mut errors = vec![];

    errors.extend(validate_left_recursion(rules));
    errors.extend(validate_non_failing_repetition(rules));

    errors.sort_by_key(|error| {
        match *error {
            Error::CustomErrorSpan { ref span, .. } => span.clone(),
            _ => unreachable!()
        }
    });

    let errors = errors.into_iter().map(|error| {
        format!("{}", error)
    }).collect::<Vec<_>>().join("\n\n");

    if errors.len() > 0 {
        panic!("grammar error\n\n{}", errors);
    }
}

fn validate_non_failing_repetition<I: Input>(rules: &Vec<ParserRule<I>>) -> Vec<Error<GrammarRule, I>> {
    rules.into_iter().filter_map(|rule| non_failing_repetition(&rule.node)).collect()
}

fn non_failing_repetition<I: Input>(node: &ParserNode<I>) -> Option<Error<GrammarRule, I>> {
    match node.expr {
        ParserExpr::Rep(ref other) => {
            if is_non_failing(&other.expr) {
                Some(Error::CustomErrorSpan {
                    message: "expression inside repetition is non-failing and will repeat \
                              infinitely".to_owned(),
                    span: node.span.clone()
                })
            } else {
                None
            }
        }
        ParserExpr::RepOnce(ref other) => {
            if is_non_failing(&other.expr) {
                Some(Error::CustomErrorSpan {
                    message: "expression inside repetition is non-failing and will repeat \
                              infinitely".to_owned(),
                    span: node.span.clone()
                })
            } else {
                None
            }
        }
        _ => None
    }
}

fn is_non_failing<I: Input>(expr: &ParserExpr<I>) -> bool {
    match *expr {
        ParserExpr::Str(ref string) => string == "",
        ParserExpr::Opt(_) => true,
        ParserExpr::Rep(_) => true,
        ParserExpr::Seq(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) && is_non_failing(&rhs.expr)
        }
        ParserExpr::Choice(ref lhs, ref rhs) => {
            is_non_failing(&lhs.expr) || is_non_failing(&rhs.expr)
        }
        _ => false
    }
}

fn validate_left_recursion<I: Input>(rules: &Vec<ParserRule<I>>) -> Vec<Error<GrammarRule, I>> {
    left_recursion(to_hash_map(rules))
}

fn to_hash_map<I: Input>(rules: &Vec<ParserRule<I>>) -> HashMap<Ident, &ParserNode<I>> {
    let mut hash_map = HashMap::new();

    for rule in rules {
        hash_map.insert(rule.name.clone(), &rule.node);
    }

    hash_map
}

fn left_recursion<I: Input>(rules: HashMap<Ident, &ParserNode<I>>) -> Vec<Error<GrammarRule, I>> {
    fn check_expr<I: Input>(
        mut names: HashSet<Ident>,
        node: &ParserNode<I>,
        rules: &HashMap<Ident, &ParserNode<I>>
    ) -> Option<Error<GrammarRule, I>> {
        match node.expr {
            ParserExpr::Ident(ref other)  => {
                if names.contains(other) {
                    return Some(Error::CustomErrorSpan {
                        message: format!("rule {} is left-recursive; pest::prec_climber might be \
                                          useful in this case", node.span.as_str()),
                        span: node.span.clone()
                    });
                }

                names.insert(other.clone());

                if let Some(node) = rules.get(other) {
                    check_expr(names, node, rules)
                } else {
                    None
                }
            },
            ParserExpr::Seq(ref lhs, ref rhs) => {
                if is_non_failing(&lhs.expr) {
                    check_expr(names, rhs, rules)
                } else {
                    check_expr(names, lhs, rules)
                }
            }
            ParserExpr::Choice(ref lhs, _) => check_expr(names, &lhs, rules),
            ParserExpr::Rep(ref node) => check_expr(names, &node, rules),
            ParserExpr::RepOnce(ref node) => check_expr(names, &node, rules),
            ParserExpr::Opt(ref node) => check_expr(names, &node, rules),
            ParserExpr::PosPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::NegPred(ref node) => check_expr(names, &node, rules),
            ParserExpr::Push(ref node) => check_expr(names, &node, rules),
            _ => None
        }
    }

    let mut errors = vec![];

    for (ref name, ref node) in &rules {
        let mut names = HashSet::new();

        names.insert((*name).clone());

        if let Some(error) = check_expr(names, node, &rules) {
            errors.push(error);
        }
    }

    errors
}

#[cfg(test)]
mod tests {
    use pest::Parser;

    use super::*;
    use super::super::parser::{GrammarParser, consume_rules};

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | let = { \"a\" }
  | ^-^
  |
  = let is a rust keyword")]
    fn rust_keyword() {
        let input = "let = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:1
  |
1 | any = { \"a\" }
  | ^-^
  |
  = any is a pest keyword")]
    fn pest_keyword() {
        let input = "any = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:13
  |
1 | a = { \"a\" } a = { \"a\" }
  |             ^
  |
  = rule a already defined")]
    fn already_defined() {
        let input = "a = { \"a\" } a = { \"a\" }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b }
  |       ^
  |
  = rule b is undefined")]
    fn undefined() {
        let input = "a = { b }";
        validate_pairs(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { a }
  |       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn simple_left_recursion() {
        let input = "a = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { b } b = { a }
  |       ^
  |
  = rule b is left-recursive; pest::prec_climber might be useful in this case

 --> 1:17
  |
1 | a = { b } b = { a }
  |                 ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn indirect_left_recursion() {
        let input = "a = { b } b = { a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:39
  |
1 | a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }
  |                                       ^
  |
  = rule a is left-recursive; pest::prec_climber might be useful in this case")]
    fn non_failing_left_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    fn valid_recursion() {
        let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"b\") ~ a }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }

    #[test]
    #[should_panic(expected = "grammar error

 --> 1:7
  |
1 | a = { (\"\")* }
  |       ^---^
  |
  = expression inside repetition is non-failing and will repeat infinitely")]
    fn non_failing_repetition() {
        let input = "a = { (\"\")* }";
        consume_rules(GrammarParser::parse_str(GrammarRule::grammar_rules, input).unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate open;

use kay::{ActorSystem, World, Networking};
use monet::glium::glutin::WindowBuilder;
use stagemaster::UserInterfaceID;
use std::any::Any;
use std::net::SocketAddr;
use std::time::Instant;

pub fn ensure_crossplatform_proper_thread<F: Fn() -> () + Send + 'static>(callback: F) {
    // Makes sure that:
    // a) on Windows we use a dummy thread with manually set stack size
    // b) on Mac/Linux we use the main thread, because we have to create the UI there

    if cfg!(windows) {
        let dummy_thread = ::std::thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .spawn(callback)
            .unwrap();
        dummy_thread.join().unwrap();
    } else {
        callback();
    }
}

pub fn first_time_open_wiki_release_page() {
    let mut dir = ::std::env::temp_dir();
    dir.push("cb_seen_wiki.txt");
    if !::std::path::Path::new(&dir).exists() {
        let url = "https://github.com/citybound/citybound/wiki/Road-&-Traffic-Prototype-1.2";
        if let Err(_err) = open::that(url) {
            println!("Please open {:?} in your browser!", url);
        };
        ::std::fs::File::create(dir).expect("should be able to create tmp file");
    }
}

use std::panic::{set_hook, PanicInfo};
use backtrace::Backtrace;
use std::fs::File;
use std::io::Write;

pub fn set_error_hook(ui_id: UserInterfaceID, mut world: World) {
    let callback: Box<FnMut(&PanicInfo)> = Box::new(move |panic_info| {

        let title = "SIMULATION BROKE :(";

        let message = match panic_info.payload().downcast_ref::<String>() {
            Some(string) => (string.clone()),
            None => {
                match panic_info.payload().downcast_ref::<&'static str>() {
                    Some(static_str) => (*static_str).to_string(),
                    None => "Weird error type".to_string(),
                }
            }
        };

        let backtrace = Backtrace::new();
        let location = format!(
            "at {}, line {}",
            panic_info.location().map(|l| l.file()).unwrap_or("unknown"),
            panic_info.location().map(|l| l.line()).unwrap_or(0)
        );

        let body = format!(
            "WHAT HAPPENED: {}\nWHERE IT HAPPENED:\n{}\nWHERE EXACTLY:\n{:?}",
            message,
            location,
            backtrace
        );

        let small_body = format!(
            "{}\n{}\nDETAILS IN cb_last_error.txt (AUTO-OPENED)",
            message,
            location
        );

        let report_guide = "HOW TO REPORT BUGS: https://github.com/citybound/citybound/wiki/How-to-report-bugs";

        println!(
            "{}\n{}\nALSO SEE cb_last_error.txt (AUTO-OPENED)",
            title,
            body
        );

        {
            if let Ok(mut file) = File::create("./cb_last_error.txt") {
                if let Err(_) = write!(file, "{}\n{}\n\nDETAILS:\n{}", title, report_guide, body) {
                    println!("Error writing error file, lol");
                }
            };
        }

        if let Err(_) = open::that("./cb_last_error.txt") {
            println!("Couldn't open error file");
        };

        ui_id.add_debug_text(
            title.to_owned().into(),
            small_body.into(),
            [1.0, 0.0, 0.0, 1.0],
            true,
            &mut world,
        );
        ui_id.on_panic(&mut world);
    });

    set_hook(unsafe { ::std::mem::transmute(callback) });
}

pub fn networking_from_env_args() -> Networking {
    println!("{:?}", ::std::env::args().collect::<Vec<_>>());

    if ::std::env::args().nth(1).is_none() {
        Networking::new(0, vec!["127.0.0.1:3500".parse().unwrap()])
    } else {
        let machine_id: u8 = ::std::env::args()
            .nth(1)
            .expect("expected machine_id")
            .parse()
            .unwrap();
        let network: Vec<SocketAddr> = ::std::env::args()
            .nth(2)
            .expect("expected network")
            .split(',')
            .map(|addr_str| addr_str.parse().unwrap())
            .collect();

        Networking::new(machine_id, network)
    }

}

pub fn build_window(machine_id: u8) -> WindowBuilder {
    WindowBuilder::new()
        .with_title(format!("Citybound (machine {})", machine_id))
        .with_dimensions(1920, 1080)
        .with_multitouch()
}

pub fn print_version(user_interface: UserInterfaceID, world: &mut World) {
    user_interface.add_debug_text(
        "Version".to_owned().into(),
        ::ENV.version.to_owned().into(),
        [0.0, 0.0, 0.0, 1.0],
        true,
        world,
    );
}

pub fn print_instance_counts(system: &mut ActorSystem, user_interface: UserInterfaceID) {
    user_interface.add_debug_text(
        "Number of actors".to_owned().into(),
        system.get_instance_counts().into(),
        [0.0, 0.0, 0.0, 1.0],
        false,
        &mut system.world(),
    );
}

pub fn print_network_turn(system: &mut ActorSystem, user_interface: UserInterfaceID) {
    user_interface.add_debug_text(
        "Networking turn".to_owned().into(),
        system.networking_debug_all_n_turns().into(),
        [0.0, 0.0, 0.0, 1.0],
        false,
        &mut system.world(),
    );
}

pub struct FrameCounter {
    last_frame: Instant,
    elapsed_ms_collected: Vec<f32>,
}

impl FrameCounter {
    pub fn new() -> FrameCounter {
        FrameCounter {
            last_frame: Instant::now(),
            elapsed_ms_collected: Vec::new(),
        }
    }

    pub fn start_frame(&mut self) {
        let elapsed_ms = self.last_frame.elapsed().as_secs() as f32 * 1000.0 +
            self.last_frame.elapsed().subsec_nanos() as f32 / 10.0E5;

        self.elapsed_ms_collected.push(elapsed_ms);

        if self.elapsed_ms_collected.len() > 10 {
            self.elapsed_ms_collected.remove(0);
        }

        self.last_frame = Instant::now();
    }

    pub fn print_fps(&self, user_interface: UserInterfaceID, world: &mut World) {
        let avg_elapsed_ms = self.elapsed_ms_collected.iter().sum::<f32>() /
            (self.elapsed_ms_collected.len() as f32);

        user_interface.add_debug_text(
            "Frame".to_owned().into(),
            format!("{:.1} FPS", 1000.0 * 1.0 / avg_elapsed_ms).into(),
            [0.0, 0.0, 0.0, 0.5],
            false,
            world,
        );
    }
}
<commit_msg>Link to new report-bugs page<commit_after>extern crate open;

use kay::{ActorSystem, World, Networking};
use monet::glium::glutin::WindowBuilder;
use stagemaster::UserInterfaceID;
use std::net::SocketAddr;
use std::time::Instant;

pub fn ensure_crossplatform_proper_thread<F: Fn() -> () + Send + 'static>(callback: F) {
    // Makes sure that:
    // a) on Windows we use a dummy thread with manually set stack size
    // b) on Mac/Linux we use the main thread, because we have to create the UI there

    if cfg!(windows) {
        let dummy_thread = ::std::thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .spawn(callback)
            .unwrap();
        dummy_thread.join().unwrap();
    } else {
        callback();
    }
}

pub fn first_time_open_wiki_release_page() {
    let mut dir = ::std::env::temp_dir();
    dir.push("cb_seen_wiki.txt");
    if !::std::path::Path::new(&dir).exists() {
        let url = "https://github.com/citybound/citybound/wiki/Road-&-Traffic-Prototype-1.2";
        if let Err(_err) = open::that(url) {
            println!("Please open {:?} in your browser!", url);
        };
        ::std::fs::File::create(dir).expect("should be able to create tmp file");
    }
}

use std::panic::{set_hook, PanicInfo};
use backtrace::Backtrace;
use std::fs::File;
use std::io::Write;

pub fn set_error_hook(ui_id: UserInterfaceID, mut world: World) {
    let callback: Box<FnMut(&PanicInfo)> = Box::new(move |panic_info| {

        let title = "SIMULATION BROKE :(";

        let message = match panic_info.payload().downcast_ref::<String>() {
            Some(string) => (string.clone()),
            None => {
                match panic_info.payload().downcast_ref::<&'static str>() {
                    Some(static_str) => (*static_str).to_string(),
                    None => "Weird error type".to_string(),
                }
            }
        };

        let backtrace = Backtrace::new();
        let location = format!(
            "at {}, line {}",
            panic_info.location().map(|l| l.file()).unwrap_or("unknown"),
            panic_info.location().map(|l| l.line()).unwrap_or(0)
        );

        let body = format!(
            "WHAT HAPPENED: {}\nWHERE IT HAPPENED:\n{}\nWHERE EXACTLY:\n{:?}",
            message,
            location,
            backtrace
        );

        let small_body = format!(
            "{}\n{}\nDETAILS IN cb_last_error.txt (AUTO-OPENED)",
            message,
            location
        );

        let report_guide = "HOW TO REPORT BUGS: https://github.com/citybound/citybound/blob/master/CONTRIBUTING.md#reporting-bugs";

        println!(
            "{}\n{}\nALSO SEE cb_last_error.txt (AUTO-OPENED)",
            title,
            body
        );

        {
            if let Ok(mut file) = File::create("./cb_last_error.txt") {
                if let Err(_) = write!(file, "{}\n{}\n\nDETAILS:\n{}", title, report_guide, body) {
                    println!("Error writing error file, lol");
                }
            };
        }

        if let Err(_) = open::that("./cb_last_error.txt") {
            println!("Couldn't open error file");
        };

        ui_id.add_debug_text(
            title.to_owned().into(),
            small_body.into(),
            [1.0, 0.0, 0.0, 1.0],
            true,
            &mut world,
        );
        ui_id.on_panic(&mut world);
    });

    set_hook(unsafe { ::std::mem::transmute(callback) });
}

pub fn networking_from_env_args() -> Networking {
    println!("{:?}", ::std::env::args().collect::<Vec<_>>());

    if ::std::env::args().nth(1).is_none() {
        Networking::new(0, vec!["127.0.0.1:3500".parse().unwrap()])
    } else {
        let machine_id: u8 = ::std::env::args()
            .nth(1)
            .expect("expected machine_id")
            .parse()
            .unwrap();
        let network: Vec<SocketAddr> = ::std::env::args()
            .nth(2)
            .expect("expected network")
            .split(',')
            .map(|addr_str| addr_str.parse().unwrap())
            .collect();

        Networking::new(machine_id, network)
    }

}

pub fn build_window(machine_id: u8) -> WindowBuilder {
    WindowBuilder::new()
        .with_title(format!("Citybound (machine {})", machine_id))
        .with_dimensions(1920, 1080)
        .with_multitouch()
}

pub fn print_version(user_interface: UserInterfaceID, world: &mut World) {
    user_interface.add_debug_text(
        "Version".to_owned().into(),
        ::ENV.version.to_owned().into(),
        [0.0, 0.0, 0.0, 1.0],
        true,
        world,
    );
}

pub fn print_instance_counts(system: &mut ActorSystem, user_interface: UserInterfaceID) {
    user_interface.add_debug_text(
        "Number of actors".to_owned().into(),
        system.get_instance_counts().into(),
        [0.0, 0.0, 0.0, 1.0],
        false,
        &mut system.world(),
    );
}

pub fn print_network_turn(system: &mut ActorSystem, user_interface: UserInterfaceID) {
    user_interface.add_debug_text(
        "Networking turn".to_owned().into(),
        system.networking_debug_all_n_turns().into(),
        [0.0, 0.0, 0.0, 1.0],
        false,
        &mut system.world(),
    );
}

pub struct FrameCounter {
    last_frame: Instant,
    elapsed_ms_collected: Vec<f32>,
}

impl FrameCounter {
    pub fn new() -> FrameCounter {
        FrameCounter {
            last_frame: Instant::now(),
            elapsed_ms_collected: Vec::new(),
        }
    }

    pub fn start_frame(&mut self) {
        let elapsed_ms = self.last_frame.elapsed().as_secs() as f32 * 1000.0 +
            self.last_frame.elapsed().subsec_nanos() as f32 / 10.0E5;

        self.elapsed_ms_collected.push(elapsed_ms);

        if self.elapsed_ms_collected.len() > 10 {
            self.elapsed_ms_collected.remove(0);
        }

        self.last_frame = Instant::now();
    }

    pub fn print_fps(&self, user_interface: UserInterfaceID, world: &mut World) {
        let avg_elapsed_ms = self.elapsed_ms_collected.iter().sum::<f32>() /
            (self.elapsed_ms_collected.len() as f32);

        user_interface.add_debug_text(
            "Frame".to_owned().into(),
            format!("{:.1} FPS", 1000.0 * 1.0 / avg_elapsed_ms).into(),
            [0.0, 0.0, 0.0, 0.5],
            false,
            world,
        );
    }
}
<|endoftext|>


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

use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use super::metadata::{read_metafile, MetaFile};
use super::{PackageIdent, PackageTarget};

use error::{Error, Result};

use tempdir::TempDir;

const INSTALL_TMP_PREFIX: &'static str = ".hab-pkg-install";

/// Return a directory which can be used as a temp dir during package install/
/// uninstall
///
/// It returns a path which is in the same parent directory as `path`
/// but with TempDir style randomization
pub fn temp_package_directory(path: &Path) -> Result<TempDir> {
    let base = path.parent().ok_or(Error::PackageUnpackFailed(
        "Could not determine parent directory for temporary package directory".to_owned(),
    ))?;
    fs::create_dir_all(base)?;
    let temp_install_prefix = path
        .file_name()
        .and_then(|f| f.to_str())
        .and_then(|dirname| Some(format!("{}-{}", INSTALL_TMP_PREFIX, dirname)))
        .ok_or(Error::PackageUnpackFailed(
            "Could not generate prefix for temporary package directory".to_owned(),
        ))?;
    Ok(TempDir::new_in(base, &temp_install_prefix)?)
}

/// Returns a list of package structs built from the contents of the given directory.
pub fn all_packages(path: &Path) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    if fs::metadata(path)?.is_dir() {
        walk_origins(&path, &mut package_list)?;
    }
    Ok(package_list)
}

/// Returns a vector of package structs built from the contents of
/// the given directory, using the given ident to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
///
pub fn package_list_for_ident(
    base_pkg_path: &Path,
    ident: &PackageIdent,
) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&ident.origin);
    package_path.push(&ident.name);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    }

    match (&ident.version, &ident.release) {
        // origin/name
        (None, _) => walk_versions(&ident.origin, &ident.name, &package_path, &mut package_list)?,
        // origin/name/version
        (Some(version), None) => {
            package_path.push(version);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }
            walk_releases(
                &ident.origin,
                &ident.name,
                &version,
                &package_path,
                &mut package_list,
            )?
        }
        // origin/name/version/release
        (Some(version), Some(release)) => {
            package_path.push(version);
            package_path.push(release);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }

            let active_target = PackageTarget::active_target();
            if let Some(new_ident) = package_ident_from_dir(
                &ident.origin,
                &ident.name,
                &version,
                active_target,
                &package_path,
            ) {
                package_list.push(new_ident.clone())
            }
        }
    }
    Ok(package_list)
}

/// Helper function for all_packages. Walks the directory at the given
/// Path for origin directories and builds on the given package list
/// by recursing into name, version, and release directories.
fn walk_origins(path: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(path)? {
        let origin_dir = entry?;
        let origin_path = origin_dir.path();
        if fs::metadata(&origin_path)?.is_dir() {
            let origin = filename_from_entry(origin_dir);
            walk_names(&origin, &origin_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_origins. Walks the direcotry at the given
/// Path for name directories and recurses into them to find version
/// and release directories.
fn walk_names(origin: &String, dir: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let name_dir = entry?;
        let name_path = name_dir.path();
        if fs::metadata(&name_path)?.is_dir() {
            let name = filename_from_entry(name_dir);
            walk_versions(&origin, &name, &name_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_names. Walks the directory at the given
/// Path and recurses into them to find release directories.
fn walk_versions(
    origin: &String,
    name: &String,
    dir: &Path,
    packages: &mut Vec<PackageIdent>,
) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let version_dir = entry?;
        let version_path = version_dir.path();
        if fs::metadata(&version_path)?.is_dir() {
            let version = filename_from_entry(version_dir);
            walk_releases(origin, name, &version, &version_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_versions. Walks the directory at the
/// given Path and constructs a Package struct if the directory is a
/// valid package directory. Any resulting packages are pushed onto
/// the given packages vector, assuming the given origin, name, and
/// version.
fn walk_releases(
    origin: &String,
    name: &String,
    version: &String,
    dir: &Path,
    packages: &mut Vec<PackageIdent>,
) -> Result<()> {
    let active_target = PackageTarget::active_target();
    for entry in fs::read_dir(dir)? {
        let release_dir = entry?;
        let release_path = release_dir.path();
        if fs::metadata(&release_path)?.is_dir() {
            if let Some(ident) =
                package_ident_from_dir(origin, name, version, active_target, &release_path)
            {
                packages.push(ident)
            }
        }
    }
    Ok(())
}

/// package_ident_from_dir returns a PackageIdent if the given
/// path contains a valid package for the given active_target.
///
/// Returns None when
///    - The directory is a temporary install directroy
///    - An error occurs reading the package metadata
///    - An error occurs reading the package target
///    - The package target doesn't match the given active target
fn package_ident_from_dir(
    origin: &String,
    name: &String,
    version: &String,
    active_target: &PackageTarget,
    dir: &Path,
) -> Option<PackageIdent> {
    let release = if let Some(rel) = dir.file_name().and_then(|f| f.to_str()) {
        rel
    } else {
        return None;
    };

    if release.starts_with(INSTALL_TMP_PREFIX) {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             because it matches installation temporary directory prefix: {}",
            dir.display()
        );
        return None;
    }

    let metafile_content = read_metafile(dir, &MetaFile::Target);
    // If there is an error reading the target metafile, then skip the candidate
    if let Err(e) = metafile_content {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             due to error reading TARGET metafile, found={}, reason={:?}",
            dir.display(),
            e,
        );
        return None;
    }

    // Any errors have been cleared, so unwrap is safe
    let metafile_content = metafile_content.unwrap();
    let install_target = PackageTarget::from_str(&metafile_content);
    // If there is an error parsing the target as a valid PackageTarget, then skip the
    // candidate
    if let Err(e) = install_target {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             due to error parsing TARGET metafile as a valid PackageTarget, \
             found={}, reason={:?}",
            dir.display(),
            e,
        );
        return None;
    }
    // Any errors have been cleared, so unwrap is safe
    let install_target = install_target.unwrap();

    // Ensure that the installed package's target matches the active `PackageTarget`,
    // otherwise skip the candidate
    if active_target == &install_target {
        return Some(PackageIdent::new(
            origin.clone(),
            name.clone(),
            Some(version.clone()),
            Some(release.to_owned()),
        ));
    } else {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate, \
             found={}, installed_target={}, active_target={}",
            dir.display(),
            install_target,
            active_target,
        );
        return None;
    }
}

fn filename_from_entry(entry: fs::DirEntry) -> String {
    return entry.file_name().to_string_lossy().into_owned().to_string();
}

fn is_existing_dir(path: &Path) -> Result<bool> {
    match fs::metadata(&path) {
        Err(err) => {
            if err.kind() == io::ErrorKind::NotFound {
                return Ok(false);
            }
            return Err(Error::from(err));
        }
        Ok(metadata) => return Ok(metadata.is_dir()),
    }
}

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

    use fs;
    use std::fs::File;
    use tempdir::TempDir;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = TempDir::new("fs-root").unwrap();
        let packages = all_packages(&package_root.path()).unwrap();

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

    #[test]
    fn not_a_dir_gives_empty_list() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let file_path = fs_root.path().join("not-a-dir");
        let _tmp_file = File::create(&file_path).unwrap();

        let packages = all_packages(&file_path).unwrap();

        assert_eq!(0, packages.len());
    }
    #[test]
    fn can_read_single_package() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let package_install = testing_package_install("core/redis", fs_root.path());

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(1, packages.len());
        assert_eq!(package_install.ident, packages[0]);
    }

    #[test]
    fn can_read_multiple_packages() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let expected = vec![
            testing_package_install("core/redis/1.0.0", fs_root.path()),
            testing_package_install("test/foobar", fs_root.path()),
            testing_package_install("core/redis/1.1.0", fs_root.path()),
        ];

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(3, packages.len());
        for p in &expected {
            assert!(packages.contains(&p.ident));
        }
    }

    #[test]
    fn create_temp_package_directory_in_same_parentdir() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_path = temp_dir.path();

        assert_eq!(&p.parent(), &temp_path.parent());
    }

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_filename = temp_dir.path().file_name().unwrap().to_str().unwrap();

        assert!(&temp_filename.starts_with(INSTALL_TMP_PREFIX));
    }

    #[test]
    fn temp_package_directory_changes() {
        let p = Path::new("/tmp/foo");
        let temp_dir1 = temp_package_directory(&p).unwrap();
        let temp_dir2 = temp_package_directory(&p).unwrap();

        assert_ne!(&temp_dir1.path(), &temp_dir2.path());
    }
}
<commit_msg>Make TMP_INSTALL_PREFIX pub again<commit_after>// Copyright (c) 2016-2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;

use super::metadata::{read_metafile, MetaFile};
use super::{PackageIdent, PackageTarget};

use error::{Error, Result};

use tempdir::TempDir;

pub const INSTALL_TMP_PREFIX: &'static str = ".hab-pkg-install";

/// Return a directory which can be used as a temp dir during package install/
/// uninstall
///
/// It returns a path which is in the same parent directory as `path`
/// but with TempDir style randomization
pub fn temp_package_directory(path: &Path) -> Result<TempDir> {
    let base = path.parent().ok_or(Error::PackageUnpackFailed(
        "Could not determine parent directory for temporary package directory".to_owned(),
    ))?;
    fs::create_dir_all(base)?;
    let temp_install_prefix = path
        .file_name()
        .and_then(|f| f.to_str())
        .and_then(|dirname| Some(format!("{}-{}", INSTALL_TMP_PREFIX, dirname)))
        .ok_or(Error::PackageUnpackFailed(
            "Could not generate prefix for temporary package directory".to_owned(),
        ))?;
    Ok(TempDir::new_in(base, &temp_install_prefix)?)
}

/// Returns a list of package structs built from the contents of the given directory.
pub fn all_packages(path: &Path) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    if fs::metadata(path)?.is_dir() {
        walk_origins(&path, &mut package_list)?;
    }
    Ok(package_list)
}

/// Returns a vector of package structs built from the contents of
/// the given directory, using the given ident to restrict the
/// search.
///
/// The search is restricted by assuming the package directory
/// structure is:
///
///    /base/ORIGIN/NAME/VERSION/RELEASE/
///
pub fn package_list_for_ident(
    base_pkg_path: &Path,
    ident: &PackageIdent,
) -> Result<Vec<PackageIdent>> {
    let mut package_list: Vec<PackageIdent> = vec![];
    let mut package_path = PathBuf::from(base_pkg_path);
    package_path.push(&ident.origin);
    package_path.push(&ident.name);

    if !is_existing_dir(&package_path)? {
        return Ok(package_list);
    }

    match (&ident.version, &ident.release) {
        // origin/name
        (None, _) => walk_versions(&ident.origin, &ident.name, &package_path, &mut package_list)?,
        // origin/name/version
        (Some(version), None) => {
            package_path.push(version);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }
            walk_releases(
                &ident.origin,
                &ident.name,
                &version,
                &package_path,
                &mut package_list,
            )?
        }
        // origin/name/version/release
        (Some(version), Some(release)) => {
            package_path.push(version);
            package_path.push(release);
            if !is_existing_dir(&package_path)? {
                return Ok(package_list);
            }

            let active_target = PackageTarget::active_target();
            if let Some(new_ident) = package_ident_from_dir(
                &ident.origin,
                &ident.name,
                &version,
                active_target,
                &package_path,
            ) {
                package_list.push(new_ident.clone())
            }
        }
    }
    Ok(package_list)
}

/// Helper function for all_packages. Walks the directory at the given
/// Path for origin directories and builds on the given package list
/// by recursing into name, version, and release directories.
fn walk_origins(path: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(path)? {
        let origin_dir = entry?;
        let origin_path = origin_dir.path();
        if fs::metadata(&origin_path)?.is_dir() {
            let origin = filename_from_entry(origin_dir);
            walk_names(&origin, &origin_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_origins. Walks the direcotry at the given
/// Path for name directories and recurses into them to find version
/// and release directories.
fn walk_names(origin: &String, dir: &Path, packages: &mut Vec<PackageIdent>) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let name_dir = entry?;
        let name_path = name_dir.path();
        if fs::metadata(&name_path)?.is_dir() {
            let name = filename_from_entry(name_dir);
            walk_versions(&origin, &name, &name_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_names. Walks the directory at the given
/// Path and recurses into them to find release directories.
fn walk_versions(
    origin: &String,
    name: &String,
    dir: &Path,
    packages: &mut Vec<PackageIdent>,
) -> Result<()> {
    for entry in fs::read_dir(dir)? {
        let version_dir = entry?;
        let version_path = version_dir.path();
        if fs::metadata(&version_path)?.is_dir() {
            let version = filename_from_entry(version_dir);
            walk_releases(origin, name, &version, &version_path, packages)?;
        }
    }
    Ok(())
}

/// Helper function for walk_versions. Walks the directory at the
/// given Path and constructs a Package struct if the directory is a
/// valid package directory. Any resulting packages are pushed onto
/// the given packages vector, assuming the given origin, name, and
/// version.
fn walk_releases(
    origin: &String,
    name: &String,
    version: &String,
    dir: &Path,
    packages: &mut Vec<PackageIdent>,
) -> Result<()> {
    let active_target = PackageTarget::active_target();
    for entry in fs::read_dir(dir)? {
        let release_dir = entry?;
        let release_path = release_dir.path();
        if fs::metadata(&release_path)?.is_dir() {
            if let Some(ident) =
                package_ident_from_dir(origin, name, version, active_target, &release_path)
            {
                packages.push(ident)
            }
        }
    }
    Ok(())
}

/// package_ident_from_dir returns a PackageIdent if the given
/// path contains a valid package for the given active_target.
///
/// Returns None when
///    - The directory is a temporary install directroy
///    - An error occurs reading the package metadata
///    - An error occurs reading the package target
///    - The package target doesn't match the given active target
fn package_ident_from_dir(
    origin: &String,
    name: &String,
    version: &String,
    active_target: &PackageTarget,
    dir: &Path,
) -> Option<PackageIdent> {
    let release = if let Some(rel) = dir.file_name().and_then(|f| f.to_str()) {
        rel
    } else {
        return None;
    };

    if release.starts_with(INSTALL_TMP_PREFIX) {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             because it matches installation temporary directory prefix: {}",
            dir.display()
        );
        return None;
    }

    let metafile_content = read_metafile(dir, &MetaFile::Target);
    // If there is an error reading the target metafile, then skip the candidate
    if let Err(e) = metafile_content {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             due to error reading TARGET metafile, found={}, reason={:?}",
            dir.display(),
            e,
        );
        return None;
    }

    // Any errors have been cleared, so unwrap is safe
    let metafile_content = metafile_content.unwrap();
    let install_target = PackageTarget::from_str(&metafile_content);
    // If there is an error parsing the target as a valid PackageTarget, then skip the
    // candidate
    if let Err(e) = install_target {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate \
             due to error parsing TARGET metafile as a valid PackageTarget, \
             found={}, reason={:?}",
            dir.display(),
            e,
        );
        return None;
    }
    // Any errors have been cleared, so unwrap is safe
    let install_target = install_target.unwrap();

    // Ensure that the installed package's target matches the active `PackageTarget`,
    // otherwise skip the candidate
    if active_target == &install_target {
        return Some(PackageIdent::new(
            origin.clone(),
            name.clone(),
            Some(version.clone()),
            Some(release.to_owned()),
        ));
    } else {
        debug!(
            "PackageInstall::package_ident_from_dir(): rejected PackageInstall candidate, \
             found={}, installed_target={}, active_target={}",
            dir.display(),
            install_target,
            active_target,
        );
        return None;
    }
}

fn filename_from_entry(entry: fs::DirEntry) -> String {
    return entry.file_name().to_string_lossy().into_owned().to_string();
}

fn is_existing_dir(path: &Path) -> Result<bool> {
    match fs::metadata(&path) {
        Err(err) => {
            if err.kind() == io::ErrorKind::NotFound {
                return Ok(false);
            }
            return Err(Error::from(err));
        }
        Ok(metadata) => return Ok(metadata.is_dir()),
    }
}

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

    use fs;
    use std::fs::File;
    use tempdir::TempDir;

    #[test]
    fn empty_dir_gives_empty_list() {
        let package_root = TempDir::new("fs-root").unwrap();
        let packages = all_packages(&package_root.path()).unwrap();

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

    #[test]
    fn not_a_dir_gives_empty_list() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let file_path = fs_root.path().join("not-a-dir");
        let _tmp_file = File::create(&file_path).unwrap();

        let packages = all_packages(&file_path).unwrap();

        assert_eq!(0, packages.len());
    }
    #[test]
    fn can_read_single_package() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let package_install = testing_package_install("core/redis", fs_root.path());

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(1, packages.len());
        assert_eq!(package_install.ident, packages[0]);
    }

    #[test]
    fn can_read_multiple_packages() {
        let fs_root = TempDir::new("fs-root").unwrap();
        let package_root = fs::pkg_root_path(Some(fs_root.path()));
        let expected = vec![
            testing_package_install("core/redis/1.0.0", fs_root.path()),
            testing_package_install("test/foobar", fs_root.path()),
            testing_package_install("core/redis/1.1.0", fs_root.path()),
        ];

        let packages = all_packages(&package_root).unwrap();

        assert_eq!(3, packages.len());
        for p in &expected {
            assert!(packages.contains(&p.ident));
        }
    }

    #[test]
    fn create_temp_package_directory_in_same_parentdir() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_path = temp_dir.path();

        assert_eq!(&p.parent(), &temp_path.parent());
    }

    #[test]
    fn temp_package_directory_starts_with_prefix() {
        let p = Path::new("/tmp/foo");
        let temp_dir = temp_package_directory(&p).unwrap();
        let temp_filename = temp_dir.path().file_name().unwrap().to_str().unwrap();

        assert!(&temp_filename.starts_with(INSTALL_TMP_PREFIX));
    }

    #[test]
    fn temp_package_directory_changes() {
        let p = Path::new("/tmp/foo");
        let temp_dir1 = temp_package_directory(&p).unwrap();
        let temp_dir2 = temp_package_directory(&p).unwrap();

        assert_ne!(&temp_dir1.path(), &temp_dir2.path());
    }
}
<|endoftext|>