<commit_before>// Code that generates a test runner to run all the tests in a crate

use log::debug;
use rustc_ast::ast::{self, Ident};
use rustc_ast::attr;
use rustc_ast::entry::{self, EntryPointType};
use rustc_ast::mut_visit::{ExpectOne, *};
use rustc_ast::ptr::P;
use rustc_expand::base::{ExtCtxt, Resolver};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::PanicStrategy;
use smallvec::{smallvec, SmallVec};

use std::{iter, mem};

struct Test {
    span: Span,
    ident: Ident,
}

struct TestCtxt<'a> {
    ext_cx: ExtCtxt<'a>,
    panic_strategy: PanicStrategy,
    def_site: Span,
    test_cases: Vec<Test>,
    reexport_test_harness_main: Option<Symbol>,
    test_runner: Option<ast::Path>,
}

// Traverse the crate, collecting all the test functions, eliding any
// existing main functions, and synthesizing a main test harness
pub fn inject(
    sess: &ParseSess,
    resolver: &mut dyn Resolver,
    should_test: bool,
    krate: &mut ast::Crate,
    span_diagnostic: &rustc_errors::Handler,
    features: &Features,
    panic_strategy: PanicStrategy,
    platform_panic_strategy: PanicStrategy,
    enable_panic_abort_tests: bool,
) {
    // Check for #![reexport_test_harness_main = "some_name"] which gives the
    // main test function the name `some_name` without hygiene. This needs to be
    // unconditional, so that the attribute is still marked as used in
    // non-test builds.
    let reexport_test_harness_main =
        attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);

    // Do this here so that the test_runner crate attribute gets marked as used
    // even in non-test builds
    let test_runner = get_test_runner(span_diagnostic, &krate);

    if should_test {
        let panic_strategy = match (panic_strategy, enable_panic_abort_tests) {
            (PanicStrategy::Abort, true) => PanicStrategy::Abort,
            (PanicStrategy::Abort, false) if panic_strategy == platform_panic_strategy => {
                // Silently allow compiling with panic=abort on these platforms,
                // but with old behavior (abort if a test fails).
                PanicStrategy::Unwind
            }
            (PanicStrategy::Abort, false) => {
                span_diagnostic.err(
                    "building tests with panic=abort is not supported \
                                     without `-Zpanic_abort_tests`",
                );
                PanicStrategy::Unwind
            }
            (PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
        };
        generate_test_harness(
            sess,
            resolver,
            reexport_test_harness_main,
            krate,
            features,
            panic_strategy,
            test_runner,
        )
    }
}

struct TestHarnessGenerator<'a> {
    cx: TestCtxt<'a>,
    tests: Vec<Test>,
}

impl<'a> MutVisitor for TestHarnessGenerator<'a> {
    fn visit_crate(&mut self, c: &mut ast::Crate) {
        noop_visit_crate(c, self);

        // Create a main function to run our tests
        c.module.items.push(mk_main(&mut self.cx));
    }

    fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        let mut item = i.into_inner();
        if is_test_case(&item) {
            debug!("this is a test item");

            let test = Test { span: item.span, ident: item.ident };
            self.tests.push(test);
        }

        // We don't want to recurse into anything other than mods, since
        // mods or tests inside of functions will break things
        if let ast::ItemKind::Mod(mut module) = item.kind {
            let tests = mem::take(&mut self.tests);
            noop_visit_mod(&mut module, self);
            let mut tests = mem::replace(&mut self.tests, tests);

            if !tests.is_empty() {
                let parent =
                    if item.id == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { item.id };
                // Create an identifier that will hygienically resolve the test
                // case name, even in another module.
                let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass(
                    module.inner,
                    AstPass::TestHarness,
                    &[],
                    Some(parent),
                );
                for test in &mut tests {
                    // See the comment on `mk_main` for why we're using
                    // `apply_mark` directly.
                    test.ident.span = test.ident.span.apply_mark(expn_id, Transparency::Opaque);
                }
                self.cx.test_cases.extend(tests);
            }
            item.kind = ast::ItemKind::Mod(module);
        }
        smallvec![P(item)]
    }

    fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
        // Do nothing.
    }
}

/// A folder used to remove any entry points (like fn main) because the harness
/// generator will provide its own
struct EntryPointCleaner {
    // Current depth in the ast
    depth: usize,
    def_site: Span,
}

impl MutVisitor for EntryPointCleaner {
    fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        self.depth += 1;
        let item = noop_flat_map_item(i, self).expect_one("noop did something");
        self.depth -= 1;

        // Remove any #[main] or #[start] from the AST so it doesn't
        // clash with the one we're going to add, but mark it as
        // #[allow(dead_code)] to avoid printing warnings.
        let item = match entry::entry_point_type(&item, self.depth) {
            EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
                .map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
                    let allow_ident = Ident::new(sym::allow, self.def_site);
                    let dc_nested = attr::mk_nested_word_item(Ident::from_str_and_span(
                        "dead_code",
                        self.def_site,
                    ));
                    let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
                    let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
                    let attrs = attrs
                        .into_iter()
                        .filter(|attr| !attr.check_name(sym::main) && !attr.check_name(sym::start))
                        .chain(iter::once(allow_dead_code))
                        .collect();

                    ast::Item { id, ident, attrs, kind, vis, span, tokens }
                }),
            EntryPointType::None | EntryPointType::OtherMain => item,
        };

        smallvec![item]
    }

    fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
        // Do nothing.
    }
}

/// Crawl over the crate, inserting test reexports and the test main function
fn generate_test_harness(
    sess: &ParseSess,
    resolver: &mut dyn Resolver,
    reexport_test_harness_main: Option<Symbol>,
    krate: &mut ast::Crate,
    features: &Features,
    panic_strategy: PanicStrategy,
    test_runner: Option<ast::Path>,
) {
    let mut econfig = ExpansionConfig::default("test".to_string());
    econfig.features = Some(features);

    let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);

    let expn_id = ext_cx.resolver.expansion_for_ast_pass(
        DUMMY_SP,
        AstPass::TestHarness,
        &[sym::main, sym::test, sym::rustc_attrs],
        None,
    );
    let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);

    // Remove the entry points
    let mut cleaner = EntryPointCleaner { depth: 0, def_site };
    cleaner.visit_crate(krate);

    let cx = TestCtxt {
        ext_cx,
        panic_strategy,
        def_site,
        test_cases: Vec::new(),
        reexport_test_harness_main,
        test_runner,
    };

    TestHarnessGenerator { cx, tests: Vec::new() }.visit_crate(krate);
}

/// Creates a function item for use as the main function of a test build.
/// This function will call the `test_runner` as specified by the crate attribute
///
/// By default this expands to
///
/// #[main]
/// pub fn main() {
///     extern crate test;
///     test::test_main_static(&[
///         &test_const1,
///         &test_const2,
///         &test_const3,
///     ]);
/// }
///
/// Most of the Ident have the usual def-site hygiene for the AST pass. The
/// exception is the `test_const`s. These have a syntax context that has two
/// opaque marks: one from the expansion of `test` or `test_case`, and one
/// generated  in `TestHarnessGenerator::flat_map_item`. When resolving this
/// identifier after failing to find a matching identifier in the root module
/// we remove the outer mark, and try resolving at its def-site, which will
/// then resolve to `test_const`.
///
/// The expansion here can be controlled by two attributes:
///
/// `reexport_test_harness_main` provides a different name for the `main`
/// function and `test_runner` provides a path that replaces
/// `test::test_main_static`.
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
    let sp = cx.def_site;
    let ecx = &cx.ext_cx;
    let test_id = Ident::new(sym::test, sp);

    let runner_name = match cx.panic_strategy {
        PanicStrategy::Unwind => "test_main_static",
        PanicStrategy::Abort => "test_main_static_abort",
    };

    // test::test_main_static(...)
    let mut test_runner = cx
        .test_runner
        .clone()
        .unwrap_or(ecx.path(sp, vec![test_id, ecx.ident_of(runner_name, sp)]));

    test_runner.span = sp;

    let test_main_path_expr = ecx.expr_path(test_runner);
    let call_test_main = ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(cx, sp)]);
    let call_test_main = ecx.stmt_expr(call_test_main);

    // extern crate test
    let test_extern_stmt =
        ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));

    // #[main]
    let main_meta = ecx.meta_word(sp, sym::main);
    let main_attr = ecx.attribute(main_meta);

    // pub fn main() { ... }
    let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));

    // If no test runner is provided we need to import the test crate
    let main_body = if cx.test_runner.is_none() {
        ecx.block(sp, vec![test_extern_stmt, call_test_main])
    } else {
        ecx.block(sp, vec![call_test_main])
    };

    let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty));
    let sig = ast::FnSig { decl, header: ast::FnHeader::default() };
    let def = ast::Defaultness::Final;
    let main = ast::ItemKind::Fn(def, sig, ast::Generics::default(), Some(main_body));

    // Honor the reexport_test_harness_main attribute
    let main_id = match cx.reexport_test_harness_main {
        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
        None => Ident::new(sym::main, sp),
    };

    let main = P(ast::Item {
        ident: main_id,
        attrs: vec![main_attr],
        id: ast::DUMMY_NODE_ID,
        kind: main,
        vis: respan(sp, ast::VisibilityKind::Public),
        span: sp,
        tokens: None,
    });

    // Integrate the new item into existing module structures.
    let main = AstFragment::Items(smallvec![main]);
    cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
}

/// Creates a slice containing every test like so:
/// &[&test1, &test2]
fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
    debug!("building test vector from {} tests", cx.test_cases.len());
    let ecx = &cx.ext_cx;

    ecx.expr_vec_slice(
        sp,
        cx.test_cases
            .iter()
            .map(|test| {
                ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident])))
            })
            .collect(),
    )
}

fn is_test_case(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::rustc_test_marker)
}

fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
    let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
    test_attr.meta_item_list().map(|meta_list| {
        if meta_list.len() != 1 {
            sd.span_fatal(test_attr.span, "`#![test_runner(..)]` accepts exactly 1 argument")
                .raise()
        }
        match meta_list[0].meta_item() {
            Some(meta_item) if meta_item.is_word() => meta_item.path.clone(),
            _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise(),
        }
    })
}
<commit_msg>defatalize get_test_runner<commit_after>// Code that generates a test runner to run all the tests in a crate

use log::debug;
use rustc_ast::ast::{self, Ident};
use rustc_ast::attr;
use rustc_ast::entry::{self, EntryPointType};
use rustc_ast::mut_visit::{ExpectOne, *};
use rustc_ast::ptr::P;
use rustc_expand::base::{ExtCtxt, Resolver};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
use rustc_feature::Features;
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::PanicStrategy;
use smallvec::{smallvec, SmallVec};

use std::{iter, mem};

struct Test {
    span: Span,
    ident: Ident,
}

struct TestCtxt<'a> {
    ext_cx: ExtCtxt<'a>,
    panic_strategy: PanicStrategy,
    def_site: Span,
    test_cases: Vec<Test>,
    reexport_test_harness_main: Option<Symbol>,
    test_runner: Option<ast::Path>,
}

// Traverse the crate, collecting all the test functions, eliding any
// existing main functions, and synthesizing a main test harness
pub fn inject(
    sess: &ParseSess,
    resolver: &mut dyn Resolver,
    should_test: bool,
    krate: &mut ast::Crate,
    span_diagnostic: &rustc_errors::Handler,
    features: &Features,
    panic_strategy: PanicStrategy,
    platform_panic_strategy: PanicStrategy,
    enable_panic_abort_tests: bool,
) {
    // Check for #![reexport_test_harness_main = "some_name"] which gives the
    // main test function the name `some_name` without hygiene. This needs to be
    // unconditional, so that the attribute is still marked as used in
    // non-test builds.
    let reexport_test_harness_main =
        attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);

    // Do this here so that the test_runner crate attribute gets marked as used
    // even in non-test builds
    let test_runner = get_test_runner(span_diagnostic, &krate);

    if should_test {
        let panic_strategy = match (panic_strategy, enable_panic_abort_tests) {
            (PanicStrategy::Abort, true) => PanicStrategy::Abort,
            (PanicStrategy::Abort, false) if panic_strategy == platform_panic_strategy => {
                // Silently allow compiling with panic=abort on these platforms,
                // but with old behavior (abort if a test fails).
                PanicStrategy::Unwind
            }
            (PanicStrategy::Abort, false) => {
                span_diagnostic.err(
                    "building tests with panic=abort is not supported \
                                     without `-Zpanic_abort_tests`",
                );
                PanicStrategy::Unwind
            }
            (PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
        };
        generate_test_harness(
            sess,
            resolver,
            reexport_test_harness_main,
            krate,
            features,
            panic_strategy,
            test_runner,
        )
    }
}

struct TestHarnessGenerator<'a> {
    cx: TestCtxt<'a>,
    tests: Vec<Test>,
}

impl<'a> MutVisitor for TestHarnessGenerator<'a> {
    fn visit_crate(&mut self, c: &mut ast::Crate) {
        noop_visit_crate(c, self);

        // Create a main function to run our tests
        c.module.items.push(mk_main(&mut self.cx));
    }

    fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        let mut item = i.into_inner();
        if is_test_case(&item) {
            debug!("this is a test item");

            let test = Test { span: item.span, ident: item.ident };
            self.tests.push(test);
        }

        // We don't want to recurse into anything other than mods, since
        // mods or tests inside of functions will break things
        if let ast::ItemKind::Mod(mut module) = item.kind {
            let tests = mem::take(&mut self.tests);
            noop_visit_mod(&mut module, self);
            let mut tests = mem::replace(&mut self.tests, tests);

            if !tests.is_empty() {
                let parent =
                    if item.id == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { item.id };
                // Create an identifier that will hygienically resolve the test
                // case name, even in another module.
                let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass(
                    module.inner,
                    AstPass::TestHarness,
                    &[],
                    Some(parent),
                );
                for test in &mut tests {
                    // See the comment on `mk_main` for why we're using
                    // `apply_mark` directly.
                    test.ident.span = test.ident.span.apply_mark(expn_id, Transparency::Opaque);
                }
                self.cx.test_cases.extend(tests);
            }
            item.kind = ast::ItemKind::Mod(module);
        }
        smallvec![P(item)]
    }

    fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
        // Do nothing.
    }
}

/// A folder used to remove any entry points (like fn main) because the harness
/// generator will provide its own
struct EntryPointCleaner {
    // Current depth in the ast
    depth: usize,
    def_site: Span,
}

impl MutVisitor for EntryPointCleaner {
    fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        self.depth += 1;
        let item = noop_flat_map_item(i, self).expect_one("noop did something");
        self.depth -= 1;

        // Remove any #[main] or #[start] from the AST so it doesn't
        // clash with the one we're going to add, but mark it as
        // #[allow(dead_code)] to avoid printing warnings.
        let item = match entry::entry_point_type(&item, self.depth) {
            EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
                .map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
                    let allow_ident = Ident::new(sym::allow, self.def_site);
                    let dc_nested = attr::mk_nested_word_item(Ident::from_str_and_span(
                        "dead_code",
                        self.def_site,
                    ));
                    let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
                    let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
                    let attrs = attrs
                        .into_iter()
                        .filter(|attr| !attr.check_name(sym::main) && !attr.check_name(sym::start))
                        .chain(iter::once(allow_dead_code))
                        .collect();

                    ast::Item { id, ident, attrs, kind, vis, span, tokens }
                }),
            EntryPointType::None | EntryPointType::OtherMain => item,
        };

        smallvec![item]
    }

    fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
        // Do nothing.
    }
}

/// Crawl over the crate, inserting test reexports and the test main function
fn generate_test_harness(
    sess: &ParseSess,
    resolver: &mut dyn Resolver,
    reexport_test_harness_main: Option<Symbol>,
    krate: &mut ast::Crate,
    features: &Features,
    panic_strategy: PanicStrategy,
    test_runner: Option<ast::Path>,
) {
    let mut econfig = ExpansionConfig::default("test".to_string());
    econfig.features = Some(features);

    let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);

    let expn_id = ext_cx.resolver.expansion_for_ast_pass(
        DUMMY_SP,
        AstPass::TestHarness,
        &[sym::main, sym::test, sym::rustc_attrs],
        None,
    );
    let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);

    // Remove the entry points
    let mut cleaner = EntryPointCleaner { depth: 0, def_site };
    cleaner.visit_crate(krate);

    let cx = TestCtxt {
        ext_cx,
        panic_strategy,
        def_site,
        test_cases: Vec::new(),
        reexport_test_harness_main,
        test_runner,
    };

    TestHarnessGenerator { cx, tests: Vec::new() }.visit_crate(krate);
}

/// Creates a function item for use as the main function of a test build.
/// This function will call the `test_runner` as specified by the crate attribute
///
/// By default this expands to
///
/// #[main]
/// pub fn main() {
///     extern crate test;
///     test::test_main_static(&[
///         &test_const1,
///         &test_const2,
///         &test_const3,
///     ]);
/// }
///
/// Most of the Ident have the usual def-site hygiene for the AST pass. The
/// exception is the `test_const`s. These have a syntax context that has two
/// opaque marks: one from the expansion of `test` or `test_case`, and one
/// generated  in `TestHarnessGenerator::flat_map_item`. When resolving this
/// identifier after failing to find a matching identifier in the root module
/// we remove the outer mark, and try resolving at its def-site, which will
/// then resolve to `test_const`.
///
/// The expansion here can be controlled by two attributes:
///
/// `reexport_test_harness_main` provides a different name for the `main`
/// function and `test_runner` provides a path that replaces
/// `test::test_main_static`.
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
    let sp = cx.def_site;
    let ecx = &cx.ext_cx;
    let test_id = Ident::new(sym::test, sp);

    let runner_name = match cx.panic_strategy {
        PanicStrategy::Unwind => "test_main_static",
        PanicStrategy::Abort => "test_main_static_abort",
    };

    // test::test_main_static(...)
    let mut test_runner = cx
        .test_runner
        .clone()
        .unwrap_or(ecx.path(sp, vec![test_id, ecx.ident_of(runner_name, sp)]));

    test_runner.span = sp;

    let test_main_path_expr = ecx.expr_path(test_runner);
    let call_test_main = ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(cx, sp)]);
    let call_test_main = ecx.stmt_expr(call_test_main);

    // extern crate test
    let test_extern_stmt =
        ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None)));

    // #[main]
    let main_meta = ecx.meta_word(sp, sym::main);
    let main_attr = ecx.attribute(main_meta);

    // pub fn main() { ... }
    let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));

    // If no test runner is provided we need to import the test crate
    let main_body = if cx.test_runner.is_none() {
        ecx.block(sp, vec![test_extern_stmt, call_test_main])
    } else {
        ecx.block(sp, vec![call_test_main])
    };

    let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty));
    let sig = ast::FnSig { decl, header: ast::FnHeader::default() };
    let def = ast::Defaultness::Final;
    let main = ast::ItemKind::Fn(def, sig, ast::Generics::default(), Some(main_body));

    // Honor the reexport_test_harness_main attribute
    let main_id = match cx.reexport_test_harness_main {
        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
        None => Ident::new(sym::main, sp),
    };

    let main = P(ast::Item {
        ident: main_id,
        attrs: vec![main_attr],
        id: ast::DUMMY_NODE_ID,
        kind: main,
        vis: respan(sp, ast::VisibilityKind::Public),
        span: sp,
        tokens: None,
    });

    // Integrate the new item into existing module structures.
    let main = AstFragment::Items(smallvec![main]);
    cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
}

/// Creates a slice containing every test like so:
/// &[&test1, &test2]
fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
    debug!("building test vector from {} tests", cx.test_cases.len());
    let ecx = &cx.ext_cx;

    ecx.expr_vec_slice(
        sp,
        cx.test_cases
            .iter()
            .map(|test| {
                ecx.expr_addr_of(test.span, ecx.expr_path(ecx.path(test.span, vec![test.ident])))
            })
            .collect(),
    )
}

fn is_test_case(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::rustc_test_marker)
}

fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
    let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
    let meta_list = test_attr.meta_item_list()?;
    let span = test_attr.span;
    match &*meta_list {
        [single] => match single.meta_item() {
            Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
            _ => sd.struct_span_err(span, "`test_runner` argument must be a path").emit(),
        },
        _ => sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit(),
    }
    None
}
<|endoftext|>


<commit_before>// Copyright © 2014 - 2015 by Optimal Computing Limited (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

use std::fmt;
use std::error::Error as StdError;
use std::io;

use log::Level;

pub struct Error {
    pub io: io::Error,
    pub message: String,
}

impl Error {
    pub fn log_level(&self) -> Level {
        match self.io.kind() {
            io::ErrorKind::NotFound => Level::Debug,
            _ => Level::Warn,
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str
    {
        self.io.description()
    }

    fn cause(&self) -> Option<&StdError> {
        self.io.cause()
    }
}

// This is for the Developer and Log files
impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!( f.write_str(&*self.description()) );
        if ! self.message.is_empty() {
            try!( write!(f, " = {}", self.message) );
        }
        if self.cause().is_some() {
            try!( write!(f, ": {:?}", self.cause().unwrap()) );
        }
        Ok(())
    }
}

// This is for the end user
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.io.kind() {
            io::ErrorKind::NotFound => {
                write!(f, "The file requested was not found.")
            }
            _ => {
                try!(write!(f, "{}: ", self.io.description()));
                self.io.fmt(f) // trust upstream?
            },
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error {
            io: err,
            message: "".to_owned()
        }
    }
}

impl<'a> From<(io::Error, &'a str)> for Error {
    fn from((err, message): (io::Error, &'a str)) -> Error {
        Error {
            io: err,
            message: message.to_owned(),
        }
    }
}
<commit_msg>Update for rust std::error::Error deprecation from rust 1.33.0<commit_after>// Copyright © 2014 - 2015 by Optimal Computing Limited (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

use std::fmt;
use std::error::Error as StdError;
use std::io;

use log::Level;

pub struct Error {
    pub io: io::Error,
    pub message: String,
}

impl Error {
    pub fn log_level(&self) -> Level {
        match self.io.kind() {
            io::ErrorKind::NotFound => Level::Debug,
            _ => Level::Warn,
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str
    {
        self.io.description()
    }

    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.io.source()
    }
}

// This is for the Developer and Log files
impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!( f.write_str(&*self.description()) );
        if ! self.message.is_empty() {
            try!( write!(f, " = {}", self.message) );
        }
        if self.source().is_some() {
            try!( write!(f, ": {:?}", self.source().unwrap()) );
        }
        Ok(())
    }
}

// This is for the end user
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.io.kind() {
            io::ErrorKind::NotFound => {
                write!(f, "The file requested was not found.")
            }
            _ => {
                try!(write!(f, "{}: ", self.io.description()));
                self.io.fmt(f) // trust upstream?
            },
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error {
            io: err,
            message: "".to_owned()
        }
    }
}

impl<'a> From<(io::Error, &'a str)> for Error {
    fn from((err, message): (io::Error, &'a str)) -> Error {
        Error {
            io: err,
            message: message.to_owned(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi::{self, GQuark};
use glib_container::GlibContainer;
use translate::ToGlibPtr;

pub struct Error {
    pointer: *mut ffi::GError
}

impl Error {
    pub fn new_literal(domain: GQuark, code: i32, message: &str) -> Option<Error> {
        let tmp_pointer = unsafe {
            ffi::g_error_new_literal(domain, code, message.to_glib_none().0)
        };

        if tmp_pointer.is_null() {
            None
        } else {
            Some(Error{pointer: tmp_pointer})
        }
    }

    pub fn release(&mut self) -> () {
        if !self.pointer.is_null() {
            unsafe { ffi::g_error_free(self.pointer) };
            self.pointer = ::std::ptr::null_mut();
        }
    }

    pub fn matches(&self, domain: GQuark, code: i32) -> bool {
        match unsafe { ffi::g_error_matches(self.pointer, domain, code) } {
            ffi::GFALSE => false,
            _ => true
        }
    }

    pub fn set(&mut self, domain: GQuark, code: i32, message: &str) -> () {
        unsafe {
            ffi::g_set_error_literal(&mut self.pointer, domain, code, message.to_glib_none().0)
        }
    }

    pub fn propagate(&mut self, other: &Error) -> () {
        unsafe { ffi::g_propagate_error(&mut self.pointer, other.pointer) }
    }
}

impl Clone for Error {
    fn clone(&self) -> Error {
        let tmp_pointer = unsafe { ffi::g_error_copy(self.pointer) };

        if tmp_pointer.is_null() {
            Error {
                pointer: ::std::ptr::null_mut()
            }
        } else {
            GlibContainer::wrap(tmp_pointer)
        }
    }
}

impl Drop for Error {
    fn drop(&mut self) {
        self.release();
    }
}

impl GlibContainer<*mut ffi::GError> for Error {
    fn wrap(pointer: *mut ffi::GError) -> Error {
        Error {
            pointer: pointer
        }
    }

    fn unwrap(&self) -> *mut ffi::GError {
        self.pointer
    }
}
<commit_msg>Add documentation<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi::{self, GQuark};
use glib_container::GlibContainer;
use translate::ToGlibPtr;

/// The Error structure contains information about an error that has occurred.
pub struct Error {
    pointer: *mut ffi::GError
}

impl Error {
    /// Creates a new GError; unlike g_error_new(), message is not a printf()-style format string.
    /// Use this function if message contains text you don't have control over, that could include
    /// printf() escape sequences.
    pub fn new_literal(domain: GQuark, code: i32, message: &str) -> Option<Error> {
        let tmp_pointer = unsafe {
            ffi::g_error_new_literal(domain, code, message.to_glib_none().0)
        };

        if tmp_pointer.is_null() {
            None
        } else {
            Some(Error{pointer: tmp_pointer})
        }
    }

    /// Frees a GError and associated resources.
    pub fn release(&mut self) -> () {
        if !self.pointer.is_null() {
            unsafe { ffi::g_error_free(self.pointer) };
            self.pointer = ::std::ptr::null_mut();
        }
    }

    /// Returns true if error matches domain and code , false otherwise. In particular, when error
    /// is NULL, false will be returned.
    ///
    /// If domain contains a FAILED (or otherwise generic) error code, you should generally not check
    /// for it explicitly, but should instead treat any not-explicitly-recognized error code as being
    /// equivalent to the FAILED code. This way, if the domain is extended in the future to provide a
    /// more specific error code for a certain case, your code will still work.
    pub fn matches(&self, domain: GQuark, code: i32) -> bool {
        match unsafe { ffi::g_error_matches(self.pointer, domain, code) } {
            ffi::GFALSE => false,
            _ => true
        }
    }

    /// Does nothing if self.pointer is NULL; if self.pointer is non-NULL, then *self.pointer must be NULL.
    /// A new GError is created and assigned to *self.pointer .
    pub fn set(&mut self, domain: GQuark, code: i32, message: &str) -> () {
        unsafe {
            ffi::g_set_error_literal(&mut self.pointer, domain, code, message.to_glib_none().0)
        }
    }

    /// If other.pointer is NULL, free src ; otherwise, moves src into *other . The error variable
    /// other.pointer points to must be NULL.
    ///
    /// Note that src is no longer valid after this call. If you want to keep using the same GError*, you
    /// need to set it to NULL after calling this function on it.
    pub fn propagate(&mut self, other: &Error) -> () {
        unsafe { ffi::g_propagate_error(&mut self.pointer, other.pointer) }
    }
}

impl Clone for Error {
    fn clone(&self) -> Error {
        let tmp_pointer = unsafe { ffi::g_error_copy(self.pointer) };

        if tmp_pointer.is_null() {
            Error {
                pointer: ::std::ptr::null_mut()
            }
        } else {
            GlibContainer::wrap(tmp_pointer)
        }
    }
}

impl Drop for Error {
    fn drop(&mut self) {
        self.release();
    }
}

impl GlibContainer<*mut ffi::GError> for Error {
    fn wrap(pointer: *mut ffi::GError) -> Error {
        Error {
            pointer: pointer
        }
    }

    fn unwrap(&self) -> *mut ffi::GError {
        self.pointer
    }
}
<|endoftext|>


<commit_before>error_chain! {
    links {
        Config      (::config::Error,          ::config::ErrorKind);
        Feed        (::feed::Error,            ::feed::ErrorKind);
        Listeners   (::feed::listeners::Error, ::feed::listeners::ErrorKind);
        Notification(::notification::Error,    ::notification::ErrorKind);
        Util        (::util::Error,            ::util::ErrorKind);
    }
}

fn get_err_msg(err: &Error) -> String {
    let causes = err
        .iter()
        .skip(1)
        .map(|e| format!("caused by: {}\n", e))
        .collect::<String>();

    format!("error: {}\n{}", err, causes)
}

/// Displays the provided error with a notification and by writing it to the terminal
pub fn report(err: &Error) {
    let msg = get_err_msg(&err);
    eprintln!("{}", msg);

    if let Some(backtrace) = err.backtrace() {
        eprintln!("{:?}", backtrace);
    }

    match ::notification::create_error(&msg) {
        Ok(_) => (),
        Err(err) => {
            let err = err.into();
            eprintln!("failed to create error notification:\n{}", get_err_msg(&err));

            if let Some(backtrace) = err.backtrace() {
                eprintln!("{:?}", backtrace);
            }
        }
    }
}<commit_msg>Move backtrace printing to function<commit_after>error_chain! {
    links {
        Config      (::config::Error,          ::config::ErrorKind);
        Feed        (::feed::Error,            ::feed::ErrorKind);
        Listeners   (::feed::listeners::Error, ::feed::listeners::ErrorKind);
        Notification(::notification::Error,    ::notification::ErrorKind);
        Util        (::util::Error,            ::util::ErrorKind);
    }
}

fn get_err_msg(err: &Error) -> String {
    let causes = err
        .iter()
        .skip(1)
        .map(|e| format!("caused by: {}\n", e))
        .collect::<String>();

    format!("error: {}\n{}", err, causes)
}

fn print_backtrace(err: &Error) {
    if let Some(backtrace) = err.backtrace() {
        eprintln!("{:?}", backtrace);
    }
}

/// Displays the provided error with a notification and by writing it to the terminal
pub fn report(err: &Error) {
    let msg = get_err_msg(&err);
    eprintln!("{}", msg);

    print_backtrace(err);

    match ::notification::create_error(&msg) {
        Ok(_) => (),
        Err(err) => {
            let err = err.into();
            eprintln!("failed to create error notification:\n{}", get_err_msg(&err));

            print_backtrace(&err);
        }
    }
}<|endoftext|>


<commit_before>//! Error type

use std::error;
use std::fmt::{self, Display, Formatter};
use std::io;
use std::str::Utf8Error;

use http::StatusCode;

/// An error occurred while trying to connect to a Stream.
#[derive(Debug)]
pub enum Error<E = Box<dyn error::Error + Send + Sync>> {
    /// An error occured while decoding gzip stream from the server.
    Gzip(io::Error),
    /// An HTTP error from the Stream.
    Http(StatusCode),
    /// Error from the underlying HTTP client while receiving an HTTP response or reading the body.
    Service(E),
    /// Twitter returned a non-UTF-8 string.
    Utf8(Utf8Error),
    /// User-defined error.
    Custom(Box<dyn error::Error + Send + Sync>),
}

impl<E> Error<E> {
    pub fn custom<C>(error: C) -> Self
    where
        C: Into<Box<dyn error::Error + Send + Sync>>,
    {
        Error::Custom(error.into())
    }
}

impl<E: error::Error + 'static> error::Error for Error<E> {
    fn description(&self) -> &str {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => e.description(),
            Http(ref status) => status.canonical_reason().unwrap_or("<unknown status code>"),
            Service(ref e) => e.description(),
            Utf8(ref e) => e.description(),
            Custom(ref e) => e.description(),
        }
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => Some(e),
            Http(_) => None,
            Service(ref e) => Some(e),
            Utf8(ref e) => Some(e),
            Custom(ref e) => Some(&**e),
        }
    }
}

impl<E: Display> Display for Error<E> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => Display::fmt(e, f),
            Http(ref code) => Display::fmt(code, f),
            Service(ref e) => Display::fmt(e, f),
            Utf8(ref e) => Display::fmt(e, f),
            Custom(ref e) => Display::fmt(e, f),
        }
    }
}
<commit_msg>Remove `Error::Custom` variant<commit_after>//! Error type

use std::error;
use std::fmt::{self, Display, Formatter};
use std::io;
use std::str::Utf8Error;

use http::StatusCode;

/// An error occurred while trying to connect to a Stream.
#[derive(Debug)]
pub enum Error<E = Box<dyn error::Error + Send + Sync>> {
    /// An error occured while decoding gzip stream from the server.
    Gzip(io::Error),
    /// An HTTP error from the Stream.
    Http(StatusCode),
    /// Error from the underlying HTTP client while receiving an HTTP response or reading the body.
    Service(E),
    /// Twitter returned a non-UTF-8 string.
    Utf8(Utf8Error),
}

impl<E: error::Error + 'static> error::Error for Error<E> {
    fn description(&self) -> &str {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => e.description(),
            Http(ref status) => status.canonical_reason().unwrap_or("<unknown status code>"),
            Service(ref e) => e.description(),
            Utf8(ref e) => e.description(),
        }
    }

    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => Some(e),
            Http(_) => None,
            Service(ref e) => Some(e),
            Utf8(ref e) => Some(e),
        }
    }
}

impl<E: Display> Display for Error<E> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        use crate::Error::*;

        match *self {
            Gzip(ref e) => Display::fmt(e, f),
            Http(ref code) => Display::fmt(code, f),
            Service(ref e) => Display::fmt(e, f),
            Utf8(ref e) => Display::fmt(e, f),
        }
    }
}
<|endoftext|>


<commit_before>use std::{io, error, fmt};
use git2;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Git(git2::Error),
    FolderNotEmpty(String, usize),
    TodoErr,
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(_) => "IO error occurred",
            Error::Git(_) => "Libgit2 error occurred",
            Error::FolderNotEmpty(_, _) => "Root folder was not empty",
            Error::TodoErr => "Todo",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
           Error::Io(ref err) => Some(err),
           Error::Git(ref err) => Some(err),
           Error::FolderNotEmpty(_, _) => None,
           Error::TodoErr => None,
       }
   }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "IO error occurred: {}", error::Error::description(err)),
            Error::Git(ref err) => write!(f, "Libgit2 error occurred: {}", error::Error::description(err)),
            Error::FolderNotEmpty(ref root, count) => write!(f, "{} was not empty: {} files exist", root, count),
            Error::TodoErr => write!(f, "TodoErr"),
        }
    }
}
<commit_msg>Deleted more code<commit_after>use std::{io, error, fmt};
use git2;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Git(git2::Error),
    FolderNotEmpty(String, usize),
    TodoErr,
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(_) => "IO error occurred",
            Error::Git(_) => "Libgit2 error occurred",
            Error::FolderNotEmpty(_, _) => "Root folder was not empty",
            Error::TodoErr => "Todo",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
           Error::Io(ref err) => Some(err),
           Error::Git(ref err) => Some(err),
           Error::FolderNotEmpty(_, _) => None,
           Error::TodoErr => None,
       }
   }
}
<|endoftext|>


<commit_before>use crate::error::ZmqError;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use std::convert::TryFrom;
use std::fmt::Display;
use std::collections::HashMap;
use tokio_util::codec::{Decoder, Encoder};

use crate::SocketType;

#[derive(Debug, Copy, Clone)]
pub(crate) enum ZmqMechanism {
    NULL,
    PLAIN,
    CURVE,
}

impl Display for ZmqMechanism {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        return match self {
            ZmqMechanism::NULL => write!(f, "NULL"),
            ZmqMechanism::PLAIN => write!(f, "PLAIN"),
            ZmqMechanism::CURVE => write!(f, "CURVE"),
        };
    }
}

impl TryFrom<Vec<u8>> for ZmqMechanism {
    type Error = ZmqError;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        let mech = value.split(|x| *x == 0x0).next().unwrap_or(b"");
        // mechanism-char = "A"-"Z" | DIGIT
        //                  | "-" | "_" | "." | "+" | %x0
        // according to https://rfc.zeromq.org/spec:23/ZMTP/
        let mechanism = unsafe { String::from_utf8_unchecked(mech.to_vec()) };
        match mechanism.as_str() {
            "NULL" => Ok(ZmqMechanism::NULL),
            "PLAIN" => Ok(ZmqMechanism::PLAIN),
            "CURVE" => Ok(ZmqMechanism::CURVE),
            _ => Err(ZmqError::OTHER("Failed to parse ZmqMechanism")),
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub(crate) struct ZmqGreeting {
    pub version: (u8, u8),
    pub mechanism: ZmqMechanism,
    pub as_server: bool,
}

impl Default for ZmqGreeting {
    fn default() -> Self {
        Self {
            version: (3, 0),
            mechanism: ZmqMechanism::NULL,
            as_server: false,
        }
    }
}

impl TryFrom<Bytes> for ZmqGreeting {
    type Error = ZmqError;

    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
        if !(value[0] == 0xff && value[9] == 0x7f) {
            return Err(ZmqError::CODEC("Failed to parse greeting"));
        }
        Ok(ZmqGreeting {
            version: (value[10], value[11]),
            mechanism: ZmqMechanism::try_from(value[12..32].to_vec())?,
            as_server: value[32] == 0x01,
        })
    }
}

impl From<ZmqGreeting> for BytesMut {
    fn from(greet: ZmqGreeting) -> Self {
        let mut data: [u8; 64] = [0; 64];
        data[0] = 0xff;
        data[9] = 0x7f;
        data[10] = greet.version.0;
        data[11] = greet.version.1;
        let mech = format!("{}", greet.mechanism);
        data[12..12 + mech.len()].copy_from_slice(mech.as_bytes());
        data[32] = greet.as_server.into();
        let mut bytes = BytesMut::new();
        bytes.extend_from_slice(&data);
        bytes
    }
}

#[derive(Debug, Clone)]
pub struct ZmqMessage {
    pub data: Bytes,
    pub more: bool
}

#[derive(Debug, Clone)]
pub(crate) enum Message {
    Greeting(ZmqGreeting),
    Command(ZmqCommand),
    Message(ZmqMessage),
}

impl Display for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Greeting(payload) => write!(f, "Greeting"),
            Message::Message(m) => write!(f, "Bin data - {}B", m.data.len()),
            Message::Command(c) => write!(f, "Command - {:?}", c),
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub(crate) enum ZmqCommandName {
    READY,
}

impl From<ZmqCommandName> for String {
    fn from(c_name: ZmqCommandName) -> Self {
        match c_name {
            ZmqCommandName::READY => { "READY".into() },
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct ZmqCommand {
    pub name: ZmqCommandName,
    pub properties: HashMap<String, String>,
}

impl ZmqCommand {
    pub fn ready(socket: SocketType) -> Self {
        let mut properties = HashMap::new();
        properties.insert("Socket-Type".into(), format!("{}", socket));
        Self {
            name: ZmqCommandName::READY,
            properties,
        }
    }
}

impl TryFrom<BytesMut> for ZmqCommand {
    type Error = ZmqError;

    fn try_from(mut buf: BytesMut) -> Result<Self, Self::Error> {
        let command_len = buf[0] as usize;
        buf.advance(1);
        // command-name-char = ALPHA according to https://rfc.zeromq.org/spec:23/ZMTP/
        let command_name =
            unsafe { String::from_utf8_unchecked(buf.split_to(command_len).to_vec()) };
        let command = match command_name.as_str() {
            "READY" => ZmqCommandName::READY,
            _ => return Err(ZmqError::CODEC("Uknown command received")),
        };
        let mut properties = HashMap::new();

        while !buf.is_empty() {
            // Collect command properties
            let prop_len = buf[0] as usize;
            buf.advance(1);
            let property = unsafe { String::from_utf8_unchecked(buf.split_to(prop_len).to_vec()) };

            use std::u32;
            let prop_val_len = unsafe {
                u32::from_be_bytes(*(buf.split_to(4).as_ptr() as *const [u8; 4])) as usize
            };
            let prop_value =
                unsafe { String::from_utf8_unchecked(buf.split_to(prop_val_len).to_vec()) };
            properties.insert(property, prop_value);
        }
        Ok(Self {
            name: command,
            properties,
        })
    }
}

impl From<ZmqCommand> for BytesMut {
    fn from(command: ZmqCommand) -> Self {
        use std::usize;
        let mut message_len = 0;

        let command_name: String = command.name.into();
        message_len += command_name.len() + 1;
        for (prop, val) in command.properties.iter() {
            message_len += prop.len() + 1;
            message_len += val.len() + 4;
        }

        let long_message = message_len > 255;

        let mut bytes = BytesMut::new();
        if long_message {
            bytes.reserve(message_len + 9);
            bytes.put_u8(0x06);
            bytes.extend_from_slice( &message_len.to_be_bytes());
        } else {
            bytes.reserve(message_len + 2);
            bytes.put_u8(0x04);
            bytes.put_u8(message_len as u8);
        };
        bytes.put_u8(command_name.len() as u8);
        bytes.extend_from_slice(command_name.as_ref());
        for (prop, val) in command.properties.iter() {
            bytes.put_u8(prop.len() as u8);
            bytes.extend_from_slice(prop.as_ref());
            bytes.put_u32(val.len() as u32);
            bytes.extend_from_slice(val.as_ref());
        }
        bytes
    }
}

#[derive(Debug)]
enum DecoderState {
    Greeting,
    FrameHeader,
    Frame(bool, usize, bool),
    End,
}

pub(crate) struct ZmqCodec {
    state: DecoderState,
}

impl ZmqCodec {
    pub fn new() -> Self {
        Self {
            state: DecoderState::Greeting,
        }
    }
}

impl Decoder for ZmqCodec {
    type Item = Message;
    type Error = ZmqError;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        dbg!(&src);
        if src.is_empty() {
            return Ok(None)
        }
        match self.state {
            DecoderState::Greeting => {
                if src.len() >= 64 {
                    self.state = DecoderState::FrameHeader;
                    Ok(Some(Message::Greeting(ZmqGreeting::try_from(
                        src.split_to(64).freeze(),
                    )?)))
                } else {
                    if src[0] == 0xff {
                        src.reserve(64);
                        Ok(None)
                    } else {
                        Err(ZmqError::CODEC("Bad first byte of greeting"))
                    }
                }
            }
            DecoderState::FrameHeader => {
                let flags = src[0];
                let command = (flags & 0b0000_0100) != 0;
                let long = (flags & 0b0000_0010) != 0;
                let more = ((flags & 0b0000_0001) != 0) | command;

                let frame_len = if !long && src.len() >= 2 {
                    let command_len = src[1] as usize;
                    src.advance(2);
                    command_len
                } else if long && src.len() >= 9 {
                    src.advance(1);
                    use std::usize;
                    usize::from_be_bytes(unsafe {
                        *(src.split_to(8).as_ptr() as *const [u8; 8])
                    })
                } else {
                    return Ok(None);
                };
                self.state = DecoderState::Frame(command, frame_len, more);
                return self.decode(src);
            }
            DecoderState::Frame(command, frame_len, more) => {
                if src.len() < frame_len {
                    src.reserve(frame_len);
                    return Ok(None);
                }
                self.state = DecoderState::FrameHeader;
                let message = if command {
                    Message::Command(ZmqCommand::try_from(src.split_to(frame_len))?)
                } else {
                    Message::Message(ZmqMessage { data: src.split_to(frame_len).freeze(), more })
                };
                return Ok(Some(message));
            }
            DecoderState::End => Err(ZmqError::NO_MESSAGE),
        }
    }
}

impl Encoder for ZmqCodec {
    type Item = Message;
    type Error = ZmqError;

    fn encode(&mut self, message: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        match message {
            Message::Greeting(payload) => dst.unsplit(payload.into()),
            Message::Message(message) => dst.extend_from_slice(&message.data),
            Message::Command(command) => dst.unsplit(command.into())
        }
        Ok(())
    }
}<commit_msg>Minor refactoring of codec<commit_after>use crate::error::ZmqError;
use bytes::{Bytes, BytesMut, Buf, BufMut};
use std::convert::TryFrom;
use std::fmt::Display;
use std::collections::HashMap;
use tokio_util::codec::{Decoder, Encoder};

use crate::SocketType;

#[derive(Debug, Copy, Clone)]
pub(crate) enum ZmqMechanism {
    NULL,
    PLAIN,
    CURVE,
}

impl Display for ZmqMechanism {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        return match self {
            ZmqMechanism::NULL => write!(f, "NULL"),
            ZmqMechanism::PLAIN => write!(f, "PLAIN"),
            ZmqMechanism::CURVE => write!(f, "CURVE"),
        };
    }
}

impl TryFrom<Vec<u8>> for ZmqMechanism {
    type Error = ZmqError;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        let mech = value.split(|x| *x == 0x0).next().unwrap_or(b"");
        // mechanism-char = "A"-"Z" | DIGIT
        //                  | "-" | "_" | "." | "+" | %x0
        // according to https://rfc.zeromq.org/spec:23/ZMTP/
        let mechanism = unsafe { String::from_utf8_unchecked(mech.to_vec()) };
        match mechanism.as_str() {
            "NULL" => Ok(ZmqMechanism::NULL),
            "PLAIN" => Ok(ZmqMechanism::PLAIN),
            "CURVE" => Ok(ZmqMechanism::CURVE),
            _ => Err(ZmqError::OTHER("Failed to parse ZmqMechanism")),
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub(crate) struct ZmqGreeting {
    pub version: (u8, u8),
    pub mechanism: ZmqMechanism,
    pub as_server: bool,
}

impl Default for ZmqGreeting {
    fn default() -> Self {
        Self {
            version: (3, 0),
            mechanism: ZmqMechanism::NULL,
            as_server: false,
        }
    }
}

impl TryFrom<Bytes> for ZmqGreeting {
    type Error = ZmqError;

    fn try_from(value: Bytes) -> Result<Self, Self::Error> {
        if !(value[0] == 0xff && value[9] == 0x7f) {
            return Err(ZmqError::CODEC("Failed to parse greeting"));
        }
        Ok(ZmqGreeting {
            version: (value[10], value[11]),
            mechanism: ZmqMechanism::try_from(value[12..32].to_vec())?,
            as_server: value[32] == 0x01,
        })
    }
}

impl From<ZmqGreeting> for BytesMut {
    fn from(greet: ZmqGreeting) -> Self {
        let mut data: [u8; 64] = [0; 64];
        data[0] = 0xff;
        data[9] = 0x7f;
        data[10] = greet.version.0;
        data[11] = greet.version.1;
        let mech = format!("{}", greet.mechanism);
        data[12..12 + mech.len()].copy_from_slice(mech.as_bytes());
        data[32] = greet.as_server.into();
        let mut bytes = BytesMut::new();
        bytes.extend_from_slice(&data);
        bytes
    }
}

#[derive(Debug, Clone)]
pub struct ZmqMessage {
    pub data: Bytes,
    pub more: bool
}

#[derive(Debug, Clone)]
pub(crate) enum Message {
    Greeting(ZmqGreeting),
    Command(ZmqCommand),
    Message(ZmqMessage),
}

impl Display for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Greeting(payload) => write!(f, "Greeting"),
            Message::Message(m) => write!(f, "Bin data - {}B", m.data.len()),
            Message::Command(c) => write!(f, "Command - {:?}", c),
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub(crate) enum ZmqCommandName {
    READY,
}

impl From<ZmqCommandName> for String {
    fn from(c_name: ZmqCommandName) -> Self {
        match c_name {
            ZmqCommandName::READY => { "READY".into() },
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct ZmqCommand {
    pub name: ZmqCommandName,
    pub properties: HashMap<String, String>,
}

impl ZmqCommand {
    pub fn ready(socket: SocketType) -> Self {
        let mut properties = HashMap::new();
        properties.insert("Socket-Type".into(), format!("{}", socket));
        Self {
            name: ZmqCommandName::READY,
            properties,
        }
    }
}

impl TryFrom<BytesMut> for ZmqCommand {
    type Error = ZmqError;

    fn try_from(mut buf: BytesMut) -> Result<Self, Self::Error> {
        let command_len = buf[0] as usize;
        buf.advance(1);
        // command-name-char = ALPHA according to https://rfc.zeromq.org/spec:23/ZMTP/
        let command_name =
            unsafe { String::from_utf8_unchecked(buf.split_to(command_len).to_vec()) };
        let command = match command_name.as_str() {
            "READY" => ZmqCommandName::READY,
            _ => return Err(ZmqError::CODEC("Uknown command received")),
        };
        let mut properties = HashMap::new();

        while !buf.is_empty() {
            // Collect command properties
            let prop_len = buf[0] as usize;
            buf.advance(1);
            let property = unsafe { String::from_utf8_unchecked(buf.split_to(prop_len).to_vec()) };

            use std::u32;
            let prop_val_len = unsafe {
                u32::from_be_bytes(*(buf.split_to(4).as_ptr() as *const [u8; 4])) as usize
            };
            let prop_value =
                unsafe { String::from_utf8_unchecked(buf.split_to(prop_val_len).to_vec()) };
            properties.insert(property, prop_value);
        }
        Ok(Self {
            name: command,
            properties,
        })
    }
}

impl From<ZmqCommand> for BytesMut {
    fn from(command: ZmqCommand) -> Self {
        use std::usize;
        let mut message_len = 0;

        let command_name: String = command.name.into();
        message_len += command_name.len() + 1;
        for (prop, val) in command.properties.iter() {
            message_len += prop.len() + 1;
            message_len += val.len() + 4;
        }

        let long_message = message_len > 255;

        let mut bytes = BytesMut::new();
        if long_message {
            bytes.reserve(message_len + 9);
            bytes.put_u8(0x06);
            bytes.extend_from_slice( &message_len.to_be_bytes());
        } else {
            bytes.reserve(message_len + 2);
            bytes.put_u8(0x04);
            bytes.put_u8(message_len as u8);
        };
        bytes.put_u8(command_name.len() as u8);
        bytes.extend_from_slice(command_name.as_ref());
        for (prop, val) in command.properties.iter() {
            bytes.put_u8(prop.len() as u8);
            bytes.extend_from_slice(prop.as_ref());
            bytes.put_u32(val.len() as u32);
            bytes.extend_from_slice(val.as_ref());
        }
        bytes
    }
}

#[derive(Debug)]
enum DecoderState {
    Greeting,
    FrameHeader,
    Frame(bool, usize, bool),
    End,
}

pub(crate) struct ZmqCodec {
    state: DecoderState,
}

impl ZmqCodec {
    pub fn new() -> Self {
        Self {
            state: DecoderState::Greeting,
        }
    }
}

impl Decoder for ZmqCodec {
    type Item = Message;
    type Error = ZmqError;

    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        dbg!(&src);
        if src.is_empty() {
            return Ok(None)
        }
        match self.state {
            DecoderState::Greeting => {
                if src.len() >= 64 {
                    self.state = DecoderState::FrameHeader;
                    Ok(Some(Message::Greeting(ZmqGreeting::try_from(
                        src.split_to(64).freeze(),
                    )?)))
                } else {
                    if src[0] == 0xff {
                        src.reserve(64);
                        Ok(None)
                    } else {
                        Err(ZmqError::CODEC("Bad first byte of greeting"))
                    }
                }
            }
            DecoderState::FrameHeader => {
                let flags = src[0];
                let command = (flags & 0b0000_0100) != 0;
                let long = (flags & 0b0000_0010) != 0;
                let more = ((flags & 0b0000_0001) != 0) | command;

                let frame_len = if !long && src.len() >= 2 {
                    src.advance(1);
                    src.get_u8() as usize
                } else if long && src.len() >= 9 {
                    src.advance(1);
                    src.get_u64() as usize
                } else {
                    return Ok(None);
                };
                self.state = DecoderState::Frame(command, frame_len, more);
                return self.decode(src);
            }
            DecoderState::Frame(command, frame_len, more) => {
                if src.len() < frame_len {
                    src.reserve(frame_len);
                    return Ok(None);
                }
                self.state = DecoderState::FrameHeader;
                let message = if command {
                    Message::Command(ZmqCommand::try_from(src.split_to(frame_len))?)
                } else {
                    Message::Message(ZmqMessage { data: src.split_to(frame_len).freeze(), more })
                };
                return Ok(Some(message));
            }
            DecoderState::End => Err(ZmqError::NO_MESSAGE),
        }
    }
}

impl Encoder for ZmqCodec {
    type Item = Message;
    type Error = ZmqError;

    fn encode(&mut self, message: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
        match message {
            Message::Greeting(payload) => dst.unsplit(payload.into()),
            Message::Message(message) => dst.extend_from_slice(&message.data),
            Message::Command(command) => dst.unsplit(command.into())
        }
        Ok(())
    }
}<|endoftext|>


<commit_before>use std::cmp::{PartialEq, Eq};
use std::ops::{Add, Mul, Div};

#[derive(Copy, Clone, Debug)]
pub struct RGBColor {
    r: f64,
    g: f64,
    b: f64,
}

impl RGBColor {
    pub fn new(_r: f64, _g: f64, _b: f64) -> RGBColor {
        assert!(0.0 <= _r && _r <= 1.0);
        assert!(0.0 <= _g && _g <= 1.0);
        assert!(0.0 <= _b && _b <= 1.0);
        RGBColor { r: _r, g: _g, b: _b }
    }

    pub fn power(&self, p: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r.powf(p), 0.0, 1.0),
            g: clamp(self.g.powf(p), 0.0, 1.0),
            b: clamp(self.b.powf(p), 0.0, 1.0),
        }
    }

    pub fn r(&self) -> f64 {
        self.r
    }

    pub fn g(&self) -> f64 {
        self.b
    }

    pub fn b(&self) -> f64 {
        self.b
    }
}

fn clamp(value: f64, min: f64, max: f64) -> f64 {
    if value < min {
        return min
    }
    if value > max {
        return max
    }
    value
}

impl Add for RGBColor {
    type Output = RGBColor;

    fn add(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: clamp(self.r + other.r, 0.0, 1.0),
            g: clamp(self.g + other.g, 0.0, 1.0),
            b: clamp(self.b + other.b, 0.0, 1.0),
        }
    }
}

impl Mul<f64> for RGBColor {
    type Output = RGBColor;

    fn mul(self, other: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r * other, 0.0, 1.0),
            g: clamp(self.g * other, 0.0, 1.0),
            b: clamp(self.b * other, 0.0, 1.0),
        }
    }
}

impl Mul<RGBColor> for f64 {
    type Output = RGBColor;

    fn mul(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: clamp(self * other.r, 0.0, 1.0),
            g: clamp(self * other.g, 0.0, 1.0),
            b: clamp(self * other.b, 0.0, 1.0),
        }
    }
}

impl Mul for RGBColor {
    type Output = RGBColor;

    fn mul(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: self.r * other.r,
            g: self.g * other.g,
            b: self.b * other.b,
        }
    }
}

impl Div<f64> for RGBColor {
    type Output = RGBColor;

    fn div(self, other: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r / other, 0.0, 1.0),
            g: clamp(self.g / other, 0.0, 1.0),
            b: clamp(self.b / other, 0.0, 1.0),
        }
    }
}

impl PartialEq for RGBColor {
    fn eq(&self, other: &RGBColor) -> bool {
        self.r == other.r && self.g == other.g && self.b == self.b
    }
}

impl Eq for RGBColor { }

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

    #[test]
    fn test_clamp() {
        assert_eq!(clamp(2.0, 1.0, 3.0), 2.0);
        assert_eq!(clamp(0.0, 1.0, 3.0), 1.0);
        assert_eq!(clamp(4.0, 1.0, 3.0), 3.0);
    }

    #[test]
    fn test_add() {
        let c1 = RGBColor::new(0.0, 0.0, 0.0);
        let c2 = RGBColor::new(0.5, 0.5, 0.5);
        let c3 = RGBColor::new(0.6, 0.7, 0.8);

        assert_eq!(c1 + c2, c2);
        assert_eq!(c1 + c3, c3);
        assert_eq!(c2 + c3, RGBColor::new(1.0, 1.0, 1.0));
    }

    #[test]
    fn test_mul() {
        let c1 = RGBColor::new(0.0, 0.0, 0.0);
        let c2 = RGBColor::new(0.5, 0.5, 0.5);
        let c3 = RGBColor::new(0.6, 0.7, 0.8);
        let c4 = RGBColor::new(1.0, 1.0, 1.0);
        let c5 = RGBColor::new(0.25, 0.25, 0.25);

        assert_eq!(c1 * c2, c1);
        assert_eq!(c2 * c4, c2);
        assert_eq!(c2 * c3, RGBColor::new(0.3, 0.35, 0.4));
        assert_eq!(c3 * c4, c3);

        assert_eq!(c1 * 2.0, c1);
        assert_eq!(2.0 * c1, c1);

        assert_eq!(c2 * 2.0, c4);
        assert_eq!(2.0 * c2, c4);

        assert_eq!(c2 * 0.5, c5);
        assert_eq!(0.5 * c2, c5);

        assert_eq!(c3 * 2.0, c4);
        assert_eq!(2.0 * c3, c4);
    }

    #[test]
    fn test_div() {
        let c = RGBColor::new(0.5, 0.5, 0.5);
        assert_eq!(c / 2.0, RGBColor::new(0.25, 0.25, 0.25));
        assert_eq!(c / 0.001, RGBColor::new(1.0, 1.0, 1.0));
    }

    #[test]
    fn test_power() {
        let c = RGBColor::new(0.5, 0.6, 0.7);
        assert_eq!(c.power(2.0), RGBColor::new(0.25, 0.36, 0.49));
    }
}
<commit_msg>setters for RGBColor<commit_after>use std::cmp::{PartialEq, Eq};
use std::ops::{Add, Mul, Div};

#[derive(Copy, Clone, Debug)]
pub struct RGBColor {
    r: f64,
    g: f64,
    b: f64,
}

impl RGBColor {
    pub fn new(_r: f64, _g: f64, _b: f64) -> RGBColor {
        assert!(0.0 <= _r && _r <= 1.0);
        assert!(0.0 <= _g && _g <= 1.0);
        assert!(0.0 <= _b && _b <= 1.0);
        RGBColor { r: _r, g: _g, b: _b }
    }

    pub fn power(&self, p: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r.powf(p), 0.0, 1.0),
            g: clamp(self.g.powf(p), 0.0, 1.0),
            b: clamp(self.b.powf(p), 0.0, 1.0),
        }
    }

    pub fn r(&self) -> f64 {
        self.r
    }

    pub fn set_r(&mut self, r: f64) {
        assert!(0.0 <= r && r <= 1.0);
        self.r = r;
    }

    pub fn g(&self) -> f64 {
        self.g
    }

    pub fn set_g(&mut self, g: f64) {
        assert!(0.0 <= g && g <= 1.0);
        self.g = g;
    }

    pub fn b(&self) -> f64 {
        self.b
    }

    pub fn set_b(&mut self, b: f64) {
        assert!(0.0 <= b && b <= 1.0);
        self.b = b;
    }
}

fn clamp(value: f64, min: f64, max: f64) -> f64 {
    if value < min {
        return min
    }
    if value > max {
        return max
    }
    value
}

impl Add for RGBColor {
    type Output = RGBColor;

    fn add(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: clamp(self.r + other.r, 0.0, 1.0),
            g: clamp(self.g + other.g, 0.0, 1.0),
            b: clamp(self.b + other.b, 0.0, 1.0),
        }
    }
}

impl Mul<f64> for RGBColor {
    type Output = RGBColor;

    fn mul(self, other: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r * other, 0.0, 1.0),
            g: clamp(self.g * other, 0.0, 1.0),
            b: clamp(self.b * other, 0.0, 1.0),
        }
    }
}

impl Mul<RGBColor> for f64 {
    type Output = RGBColor;

    fn mul(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: clamp(self * other.r, 0.0, 1.0),
            g: clamp(self * other.g, 0.0, 1.0),
            b: clamp(self * other.b, 0.0, 1.0),
        }
    }
}

impl Mul for RGBColor {
    type Output = RGBColor;

    fn mul(self, other: RGBColor) -> RGBColor {
        RGBColor {
            r: self.r * other.r,
            g: self.g * other.g,
            b: self.b * other.b,
        }
    }
}

impl Div<f64> for RGBColor {
    type Output = RGBColor;

    fn div(self, other: f64) -> RGBColor {
        RGBColor {
            r: clamp(self.r / other, 0.0, 1.0),
            g: clamp(self.g / other, 0.0, 1.0),
            b: clamp(self.b / other, 0.0, 1.0),
        }
    }
}

impl PartialEq for RGBColor {
    fn eq(&self, other: &RGBColor) -> bool {
        self.r == other.r && self.g == other.g && self.b == self.b
    }
}

impl Eq for RGBColor { }

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

    #[test]
    fn test_clamp() {
        assert_eq!(clamp(2.0, 1.0, 3.0), 2.0);
        assert_eq!(clamp(0.0, 1.0, 3.0), 1.0);
        assert_eq!(clamp(4.0, 1.0, 3.0), 3.0);
    }

    #[test]
    fn test_getter_and_setter() {
        let mut c = RGBColor::new(0.5, 0.6, 0.7);
        assert_eq!(c.r(), 0.5);
        assert_eq!(c.g(), 0.6);
        assert_eq!(c.b(), 0.7);

        c.set_r(0.0);
        c.set_g(0.0);
        c.set_b(0.0);
        assert_eq!(c.r(), 0.0);
        assert_eq!(c.g(), 0.0);
        assert_eq!(c.b(), 0.0);
    }

    #[test]
    fn test_add() {
        let c1 = RGBColor::new(0.0, 0.0, 0.0);
        let c2 = RGBColor::new(0.5, 0.5, 0.5);
        let c3 = RGBColor::new(0.6, 0.7, 0.8);

        assert_eq!(c1 + c2, c2);
        assert_eq!(c1 + c3, c3);
        assert_eq!(c2 + c3, RGBColor::new(1.0, 1.0, 1.0));
    }

    #[test]
    fn test_mul() {
        let c1 = RGBColor::new(0.0, 0.0, 0.0);
        let c2 = RGBColor::new(0.5, 0.5, 0.5);
        let c3 = RGBColor::new(0.6, 0.7, 0.8);
        let c4 = RGBColor::new(1.0, 1.0, 1.0);
        let c5 = RGBColor::new(0.25, 0.25, 0.25);

        assert_eq!(c1 * c2, c1);
        assert_eq!(c2 * c4, c2);
        assert_eq!(c2 * c3, RGBColor::new(0.3, 0.35, 0.4));
        assert_eq!(c3 * c4, c3);

        assert_eq!(c1 * 2.0, c1);
        assert_eq!(2.0 * c1, c1);

        assert_eq!(c2 * 2.0, c4);
        assert_eq!(2.0 * c2, c4);

        assert_eq!(c2 * 0.5, c5);
        assert_eq!(0.5 * c2, c5);

        assert_eq!(c3 * 2.0, c4);
        assert_eq!(2.0 * c3, c4);
    }

    #[test]
    fn test_div() {
        let c = RGBColor::new(0.5, 0.5, 0.5);
        assert_eq!(c / 2.0, RGBColor::new(0.25, 0.25, 0.25));
        assert_eq!(c / 0.001, RGBColor::new(1.0, 1.0, 1.0));
    }

    #[test]
    fn test_power() {
        let c = RGBColor::new(0.5, 0.6, 0.7);
        assert_eq!(c.power(2.0), RGBColor::new(0.25, 0.36, 0.49));
    }
}
<|endoftext|>


<commit_before>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).unwrap().st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
            #[cfg(target_arch="mips")]
            l_sysid: 0,
            #[cfg(target_arch="mips")]
            pad: [0; 4],
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "WRITE".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "READ".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").unwrap();
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()))
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<commit_msg>Don't test on mips; replace unwrap for expect<commit_after>use nix::errno::*;
use nix::fcntl::{open, openat, readlink, readlinkat, renameat, OFlag};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use nix::Error;
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;
use tempfile::{self, NamedTempFile};

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()).unwrap();
    let fd = openat(
        dirfd,
        tmp.path().file_name().unwrap(),
        OFlag::O_RDONLY,
        Mode::empty(),
    )
    .unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(
        renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
        Error::Sys(Errno::ENOENT)
    );
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(
        readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
        expected_dir
    );
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(
            tmp.as_raw_fd(),
            Some(&mut offset),
            wr,
            None,
            2,
            SpliceFFlags::empty(),
        )
        .unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    #[cfg(not(target_arch = "mips"))]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).expect("fstat failed").st_ino as usize;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "WRITE".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
        assert_eq!(
            Some(("OFDLCK".to_string(), "READ".to_string())),
            lock_info(inode)
        );

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: usize) -> Option<(String, String)> {
        let file = File::open("/proc/locks").expect("open /proc/locks failed");
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: usize = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()));
            }
        }
        None
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_env = "uclibc",
    target_env = "freebsd"
))]
mod test_posix_fadvise {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::os::unix::io::{AsRawFd, RawFd};
    use tempfile::NamedTempFile;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno =
            posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    any(target_os = "wasi", target_env = "wasi"),
    target_os = "freebsd"
))]
mod test_posix_fallocate {

    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;
    use std::{
        io::Read,
        os::unix::io::{AsRawFd, RawFd},
    };
    use tempfile::NamedTempFile;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL) | Sys(Errno::ENODEV) | Sys(Errno::ESPIPE) | Sys(Errno::EBADF) => (),
            errno => panic!("unexpected errno {}", errno,),
        }
    }
}
<|endoftext|>