<commit_before>// xfail-pretty

fn id(x: bool) -> bool { x }

fn call_id() {
    let c <- fail;
    id(c);
}

fn call_id_2() { id(true) && id(ret); }

fn call_id_3() { id(ret) && id(ret); }

fn call_id_4() { while id(break) { } }

fn bind_id_1() { bind id(fail); }

fn bind_id_2() { bind id(ret); }

iter put_break() -> int {
    while true { put break; }
}

fn fail_fail() { fail fail; }

fn log_fail() { log_err fail; }

fn log_ret() { log_err ret; }

fn log_break() { while true { log_err break; } }

fn log_cont() { do { log_err cont; } while false }

fn ret_ret() -> int { ret (ret 2) + 3; }

fn ret_guard() {
    alt 2 {
      x when (ret) { x; }
    }
}

fn fail_then_concat() {
    let x = [], y = [3];
    fail;
    x += y;
    "good" + "bye";
}

fn main() {}
<commit_msg>Test for #924<commit_after>// xfail-pretty

fn id(x: bool) -> bool { x }

fn call_id() {
    let c <- fail;
    id(c);
}

fn call_id_2() { id(true) && id(ret); }

fn call_id_3() { id(ret) && id(ret); }

fn call_id_4() { while id(break) { } }

fn bind_id_1() { bind id(fail); }

fn bind_id_2() { bind id(ret); }

iter put_break() -> int {
    while true { put break; }
}

fn fail_fail() { fail fail; }

fn log_fail() { log_err fail; }

fn log_ret() { log_err ret; }

fn log_break() { while true { log_err break; } }

fn log_cont() { do { log_err cont; } while false }

fn ret_ret() -> int { ret (ret 2) + 3; }

fn ret_guard() {
    alt 2 {
      x when (ret) { x; }
    }
}

fn rec_ret() { let _r = {c: ret}; }

fn vec_ret() { let _v = [1, 2, ret, 4]; }

fn fail_then_concat() {
    let x = [], y = [3];
    fail;
    x += y;
    "good" + "bye";
}

fn main() {
  // Call the functions that don't fail.
  rec_ret();
  vec_ret();
  ret_ret();
  log_ret();
  call_id_2();
  call_id_3();
  call_id_4();
  bind_id_2();
  ret_guard();
}
<|endoftext|>


<commit_before>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

use super::{NapiAny, NapiValue, NapiValueInternal};

#[derive(Clone, Copy, Debug)]
pub struct NapiObject<'a> {
    value: sys::napi_value,
    env: &'a NapiEnv,
}

impl<'a> NapiObject<'a> {
    pub fn new(env: &'a NapiEnv) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_object(env.as_sys_env(), &mut value)
        })?;

        Ok(Self { value, env })
    }

    pub fn prototype(&self) -> NapiResult<NapiAny> {
        let mut result = ptr::null_mut();

        self.env.handle_status(unsafe {
            sys::napi_get_prototype(
                self.env().as_sys_env(),
                self.value,
                &mut result,
            )
        })?;

        Ok(NapiAny::with_value(self.env(), result))
    }
}

impl<'a> NapiValue for NapiObject<'a> {
    fn as_sys_value(&self) -> sys::napi_value {
        self.value
    }

    fn env(&self) -> &NapiEnv {
        self.env
    }
}

pub fn construct<'a>(
    value: sys::napi_value,
    env: &'a NapiEnv,
) -> NapiObject<'a> {
    NapiObject { value, env }
}

impl<'a> NapiValueInternal<'a> for NapiObject<'a> {
    fn construct(env: &'a NapiEnv, value: sys::napi_value) -> Self {
        Self { env, value }
    }
}
<commit_msg>fix(napi): remove unused function<commit_after>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

use super::{NapiAny, NapiValue, NapiValueInternal};

#[derive(Clone, Copy, Debug)]
pub struct NapiObject<'a> {
    value: sys::napi_value,
    env: &'a NapiEnv,
}

impl<'a> NapiObject<'a> {
    pub fn new(env: &'a NapiEnv) -> NapiResult<Self> {
        let mut value = ptr::null_mut();
        env.handle_status(unsafe {
            sys::napi_create_object(env.as_sys_env(), &mut value)
        })?;

        Ok(Self { value, env })
    }

    pub fn prototype(&self) -> NapiResult<NapiAny> {
        let mut result = ptr::null_mut();

        self.env.handle_status(unsafe {
            sys::napi_get_prototype(
                self.env().as_sys_env(),
                self.value,
                &mut result,
            )
        })?;

        Ok(NapiAny::with_value(self.env(), result))
    }
}

impl<'a> NapiValue for NapiObject<'a> {
    fn as_sys_value(&self) -> sys::napi_value {
        self.value
    }

    fn env(&self) -> &NapiEnv {
        self.env
    }
}

impl<'a> NapiValueInternal<'a> for NapiObject<'a> {
    fn construct(env: &'a NapiEnv, value: sys::napi_value) -> Self {
        Self { env, value }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(rustc_private, plugin_registrar)]

extern crate syntax;
extern crate rustc;

use syntax::ast::{MetaItem, Expr};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic::*;
use syntax::ext::deriving::generic::ty::*;
use syntax::parse::token;
use syntax::ptr::P;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("derive_Rand"),
                                  base::MultiDecorator(Box::new(expand_deriving_rand)));
}

pub fn expand_deriving_rand(cx: &mut ExtCtxt,
                            span: Span,
                            mitem: &MetaItem,
                            item: &Annotatable,
                            push: &mut FnMut(Annotatable)) {
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("rand", "Rand")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        methods: vec!(
            MethodDef {
                name: "rand",
                is_unsafe: false,
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("R",
                                  vec!( Path::new(vec!("rand", "Rng")) )))
                },
                explicit_self: None,
                args: vec!(
                    Ptr(Box::new(Literal(Path::new_local("R"))),
                        Borrowed(None, ast::MutMutable))
                ),
                ret_ty: Self_,
                attributes: Vec::new(),
                combine_substructure: combine_substructure(Box::new(|a, b, c| {
                    rand_substructure(a, b, c)
                }))
            }
        ),
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, &item, &mut |i| push(i))
}

fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
    let rng = if substr.nonself_args.len() == 1 {
        &substr.nonself_args[0]
    } else {
        cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
    };
    let rand_ident = vec!(
        cx.ident_of("rand"),
        cx.ident_of("Rand"),
        cx.ident_of("rand")
    );
    let rand_call = |cx: &mut ExtCtxt, span| {
        cx.expr_call_global(span,
                            rand_ident.clone(),
                            vec!(rng.clone()))
    };

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let path = cx.path_ident(trait_span, substr.type_ident);
            rand_thing(cx, trait_span, path, summary, rand_call)
        }
        StaticEnum(_, ref variants) => {
            if variants.is_empty() {
                cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
                // let compilation continue
                return cx.expr_usize(trait_span, 0);
            }

            let variant_count = cx.expr_usize(trait_span, variants.len());

            let rand_name = cx.path_all(trait_span,
                                        true,
                                        rand_ident.clone(),
                                        Vec::new(),
                                        Vec::new(),
                                        Vec::new());
            let rand_name = cx.expr_path(rand_name);

            // ::rand::Rand::rand(rng)
            let rv_call = cx.expr_call(trait_span,
                                       rand_name,
                                       vec!(rng.clone()));

            // need to specify the usize-ness of the random number
            let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
            let value_ident = cx.ident_of("__value");
            let let_statement = cx.stmt_let_typed(trait_span,
                                                  false,
                                                  value_ident,
                                                  usize_ty,
                                                  rv_call);

            // rand() % variants.len()
            let value_ref = cx.expr_ident(trait_span, value_ident);
            let rand_variant = cx.expr_binary(trait_span,
                                              ast::BiRem,
                                              value_ref,
                                              variant_count);

            let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
                let i_expr = cx.expr_usize(v_span, i);
                let pat = cx.pat_lit(v_span, i_expr);

                let path = cx.path(v_span, vec![substr.type_ident, ident]);
                let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
                cx.arm(v_span, vec!( pat ), thing)
            }).collect::<Vec<ast::Arm> >();

            // _ => {} at the end. Should never occur
            arms.push(cx.arm_unreachable(trait_span));

            let match_expr = cx.expr_match(trait_span, rand_variant, arms);

            let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
            cx.expr_block(block)
        }
        _ => cx.bug("Non-static method in `derive(Rand)`")
    };

    fn rand_thing<F>(cx: &mut ExtCtxt,
                     trait_span: Span,
                     ctor_path: ast::Path,
                     summary: &StaticFields,
                     mut rand_call: F)
                     -> P<Expr> where
        F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
    {
        let path = cx.expr_path(ctor_path.clone());
        match *summary {
            Unnamed(ref fields) => {
                if fields.is_empty() {
                    path
                } else {
                    let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
                    cx.expr_call(trait_span, path, exprs)
                }
            }
            Named(ref fields) => {
                let rand_fields = fields.iter().map(|&(ident, span)| {
                    let e = rand_call(cx, span);
                    cx.field_imm(span, ident, e)
                }).collect();
                cx.expr_struct(trait_span, ctor_path, rand_fields)
            }
        }
    }
}
<commit_msg>Update rand_macros to use the rustc_plugin crate<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(rustc_private, plugin_registrar)]

extern crate syntax;
extern crate rustc_plugin;

use syntax::ast::{MetaItem, Expr};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic::*;
use syntax::ext::deriving::generic::ty::*;
use syntax::parse::token;
use syntax::ptr::P;
use rustc_plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("derive_Rand"),
                                  base::MultiDecorator(Box::new(expand_deriving_rand)));
}

pub fn expand_deriving_rand(cx: &mut ExtCtxt,
                            span: Span,
                            mitem: &MetaItem,
                            item: &Annotatable,
                            push: &mut FnMut(Annotatable)) {
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("rand", "Rand")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        methods: vec!(
            MethodDef {
                name: "rand",
                is_unsafe: false,
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("R",
                                  vec!( Path::new(vec!("rand", "Rng")) )))
                },
                explicit_self: None,
                args: vec!(
                    Ptr(Box::new(Literal(Path::new_local("R"))),
                        Borrowed(None, ast::MutMutable))
                ),
                ret_ty: Self_,
                attributes: Vec::new(),
                combine_substructure: combine_substructure(Box::new(|a, b, c| {
                    rand_substructure(a, b, c)
                }))
            }
        ),
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, &item, &mut |i| push(i))
}

fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
    let rng = if substr.nonself_args.len() == 1 {
        &substr.nonself_args[0]
    } else {
        cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
    };
    let rand_ident = vec!(
        cx.ident_of("rand"),
        cx.ident_of("Rand"),
        cx.ident_of("rand")
    );
    let rand_call = |cx: &mut ExtCtxt, span| {
        cx.expr_call_global(span,
                            rand_ident.clone(),
                            vec!(rng.clone()))
    };

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let path = cx.path_ident(trait_span, substr.type_ident);
            rand_thing(cx, trait_span, path, summary, rand_call)
        }
        StaticEnum(_, ref variants) => {
            if variants.is_empty() {
                cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
                // let compilation continue
                return cx.expr_usize(trait_span, 0);
            }

            let variant_count = cx.expr_usize(trait_span, variants.len());

            let rand_name = cx.path_all(trait_span,
                                        true,
                                        rand_ident.clone(),
                                        Vec::new(),
                                        Vec::new(),
                                        Vec::new());
            let rand_name = cx.expr_path(rand_name);

            // ::rand::Rand::rand(rng)
            let rv_call = cx.expr_call(trait_span,
                                       rand_name,
                                       vec!(rng.clone()));

            // need to specify the usize-ness of the random number
            let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
            let value_ident = cx.ident_of("__value");
            let let_statement = cx.stmt_let_typed(trait_span,
                                                  false,
                                                  value_ident,
                                                  usize_ty,
                                                  rv_call);

            // rand() % variants.len()
            let value_ref = cx.expr_ident(trait_span, value_ident);
            let rand_variant = cx.expr_binary(trait_span,
                                              ast::BiRem,
                                              value_ref,
                                              variant_count);

            let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
                let i_expr = cx.expr_usize(v_span, i);
                let pat = cx.pat_lit(v_span, i_expr);

                let path = cx.path(v_span, vec![substr.type_ident, ident]);
                let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
                cx.arm(v_span, vec!( pat ), thing)
            }).collect::<Vec<ast::Arm> >();

            // _ => {} at the end. Should never occur
            arms.push(cx.arm_unreachable(trait_span));

            let match_expr = cx.expr_match(trait_span, rand_variant, arms);

            let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
            cx.expr_block(block)
        }
        _ => cx.bug("Non-static method in `derive(Rand)`")
    };

    fn rand_thing<F>(cx: &mut ExtCtxt,
                     trait_span: Span,
                     ctor_path: ast::Path,
                     summary: &StaticFields,
                     mut rand_call: F)
                     -> P<Expr> where
        F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
    {
        let path = cx.expr_path(ctor_path.clone());
        match *summary {
            Unnamed(ref fields) => {
                if fields.is_empty() {
                    path
                } else {
                    let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
                    cx.expr_call(trait_span, path, exprs)
                }
            }
            Named(ref fields) => {
                let rand_fields = fields.iter().map(|&(ident, span)| {
                    let e = rand_call(cx, span);
                    cx.field_imm(span, ident, e)
                }).collect();
                cx.expr_struct(trait_span, ctor_path, rand_fields)
            }
        }
    }
}
<|endoftext|>


<commit_before>//! The root scope of all timely dataflow computations.

use std::rc::Rc;
use std::cell::RefCell;

use progress::timestamp::RootTimestamp;
use progress::{Timestamp, Operate, Subgraph};
use timely_communication::{Allocate, Data};
use {Push, Pull};

use super::{ScopeParent, Child};

/// A `Root` is the entry point to a timely dataflow computation. It wraps a `Allocate`,
/// and has a list of child `Operate`s. The primary intended use of `Root` is through its
/// implementation of the `Scope` trait.
pub struct Root<A: Allocate> {
    allocator: Rc<RefCell<A>>,
    identifiers: Rc<RefCell<usize>>,
    dataflows: Rc<RefCell<Vec<Wrapper>>>,
    dataflow_counter: Rc<RefCell<usize>>,
}

impl<A: Allocate> Root<A> {
    /// Allocates a new `Root` bound to a channel allocator.
    pub fn new(c: A) -> Root<A> {
        let mut result = Root {
            allocator: Rc::new(RefCell::new(c)),
            identifiers: Rc::new(RefCell::new(0)),
            dataflows: Rc::new(RefCell::new(Vec::new())),
            dataflow_counter: Rc::new(RefCell::new(0)),
        };

        // LOGGING
        if cfg!(feature = "logging") {
            ::logging::initialize(&mut result);
        }

        result
    }

    /// Performs one step of the computation.
    ///
    /// A step gives each dataflow operator a chance to run, and is the
    /// main way to ensure that a computation procedes.
    pub fn step(&mut self) -> bool {

        if cfg!(feature = "logging") {
            ::logging::flush_logs();
        }

        let mut active = false;
        for dataflow in self.dataflows.borrow_mut().iter_mut() {
            let sub_active = dataflow.step();
            active = active || sub_active;
        }

        // discard completed dataflows.
        self.dataflows.borrow_mut().retain(|dataflow| dataflow.active());

        active
    }
    /// Calls `self.step()` as long as `func` evaluates to true.
    pub fn step_while<F: FnMut()->bool>(&mut self, mut func: F) {
        while func() { self.step(); }
    }

    /// The index of the worker out of its peers.
    pub fn index(&self) -> usize { self.allocator.borrow().index() }
    /// The total number of peer workers.
    pub fn peers(&self) -> usize { self.allocator.borrow().peers() }

    /// Introduces a new dataflow constructed in the supplied closure.
    ///
    /// NOTE: This is the same code as in the `scoped` method for scopes, but it has been extracted
    /// so that the `Root` type does not need to implement `Scope`.
    pub fn dataflow<T: Timestamp, R, F:FnOnce(&mut Child<Self, T>)->R>(&mut self, func: F) -> R {

        let addr = vec![self.allocator.borrow().index()];
        let dataflow_index = self.allocate_dataflow_index();
        let subscope = Subgraph::new_from(&mut (*self.allocator.borrow_mut()), dataflow_index, addr);
        let subscope = RefCell::new(subscope);

        let result = {
            let mut builder = Child {
                subgraph: &subscope,
                parent: self.clone(),
            };
            func(&mut builder)
        };

        let mut operator = subscope.into_inner();

        operator.get_internal_summary();
        operator.set_external_summary(Vec::new(), &mut []);

        let wrapper = Wrapper {
            index: dataflow_index,
            operate: Some(Box::new(operator)),
            resources: None,
        };
        self.dataflows.borrow_mut().push(wrapper);

        result
    }

    /// Manually construct a dataflow.
    pub fn construct_dataflow<O: Operate<RootTimestamp>+'static, F: FnOnce(usize)->O>(&mut self, func: F) {
        let index = self.allocate_dataflow_index();
        let mut operator = func(index);

        // prepare progress tracking for operator.
        operator.get_internal_summary();
        operator.set_external_summary(Vec::new(), &mut []);

        let wrapper = Wrapper {
            index: index,
            operate: Some(Box::new(operator)),
            resources: None,
        };
        self.dataflows.borrow_mut().push(wrapper);
    }

    // sane way to get new dataflow identifiers; used to be self.dataflows.len(). =/
    fn allocate_dataflow_index(&mut self) -> usize {
        *self.dataflow_counter.borrow_mut() += 1;
        *self.dataflow_counter.borrow() - 1
    }
}

impl<A: Allocate> ScopeParent for Root<A> {
    type Timestamp = RootTimestamp;

    fn new_identifier(&mut self) -> usize {
        *self.identifiers.borrow_mut() += 1;
        *self.identifiers.borrow() - 1
    }
}

impl<A: Allocate> Allocate for Root<A> {
    fn index(&self) -> usize { self.allocator.borrow().index() }
    fn peers(&self) -> usize { self.allocator.borrow().peers() }
    fn allocate<D: Data>(&mut self) -> (Vec<Box<Push<D>>>, Box<Pull<D>>) {
        self.allocator.borrow_mut().allocate()
    }
}

impl<A: Allocate> Clone for Root<A> {
    fn clone(&self) -> Self {
        Root {
            allocator: self.allocator.clone(),
            identifiers: self.identifiers.clone(),
            dataflows: self.dataflows.clone(),
            dataflow_counter: self.dataflow_counter.clone(),
        }
    }
}

struct Wrapper {
    index: usize,
    operate: Option<Box<Operate<RootTimestamp>>>,
    resources: Option<Box<Drop>>,
}

impl Wrapper {
    fn step(&mut self) -> bool {
        self.operate.as_mut().map(|op| op.pull_internal_progress(&mut [], &mut [], &mut [])).unwrap_or(false)
    }
    fn active(&self) -> bool { self.operate.is_some() }
}

impl Drop for Wrapper {
    fn drop(&mut self) {
        println!("dropping dataflow {:?}", self.index);
        // ensure drop order
        self.operate = None;
        self.resources = None;
    }
}
<commit_msg>updated documentation to reflect role of root<commit_after>//! The root scope of all timely dataflow computations.

use std::rc::Rc;
use std::cell::RefCell;

use progress::timestamp::RootTimestamp;
use progress::{Timestamp, Operate, Subgraph};
use timely_communication::{Allocate, Data};
use {Push, Pull};

use super::{ScopeParent, Child};

/// A `Root` is the entry point to a timely dataflow computation. It wraps a `Allocate`,
/// and has a list of child `Operate`s.
pub struct Root<A: Allocate> {
    allocator: Rc<RefCell<A>>,
    identifiers: Rc<RefCell<usize>>,
    dataflows: Rc<RefCell<Vec<Wrapper>>>,
    dataflow_counter: Rc<RefCell<usize>>,
}

impl<A: Allocate> Root<A> {
    /// Allocates a new `Root` bound to a channel allocator.
    pub fn new(c: A) -> Root<A> {
        let mut result = Root {
            allocator: Rc::new(RefCell::new(c)),
            identifiers: Rc::new(RefCell::new(0)),
            dataflows: Rc::new(RefCell::new(Vec::new())),
            dataflow_counter: Rc::new(RefCell::new(0)),
        };

        // LOGGING
        if cfg!(feature = "logging") {
            ::logging::initialize(&mut result);
        }

        result
    }

    /// Performs one step of the computation.
    ///
    /// A step gives each dataflow operator a chance to run, and is the
    /// main way to ensure that a computation procedes.
    pub fn step(&mut self) -> bool {

        if cfg!(feature = "logging") {
            ::logging::flush_logs();
        }

        let mut active = false;
        for dataflow in self.dataflows.borrow_mut().iter_mut() {
            let sub_active = dataflow.step();
            active = active || sub_active;
        }

        // discard completed dataflows.
        self.dataflows.borrow_mut().retain(|dataflow| dataflow.active());

        active
    }
    /// Calls `self.step()` as long as `func` evaluates to true.
    pub fn step_while<F: FnMut()->bool>(&mut self, mut func: F) {
        while func() { self.step(); }
    }

    /// The index of the worker out of its peers.
    pub fn index(&self) -> usize { self.allocator.borrow().index() }
    /// The total number of peer workers.
    pub fn peers(&self) -> usize { self.allocator.borrow().peers() }

    /// Introduces a new dataflow constructed in the supplied closure.
    ///
    /// NOTE: This is the same code as in the `scoped` method for scopes, but it has been extracted
    /// so that the `Root` type does not need to implement `Scope`.
    pub fn dataflow<T: Timestamp, R, F:FnOnce(&mut Child<Self, T>)->R>(&mut self, func: F) -> R {

        let addr = vec![self.allocator.borrow().index()];
        let dataflow_index = self.allocate_dataflow_index();
        let subscope = Subgraph::new_from(&mut (*self.allocator.borrow_mut()), dataflow_index, addr);
        let subscope = RefCell::new(subscope);

        let result = {
            let mut builder = Child {
                subgraph: &subscope,
                parent: self.clone(),
            };
            func(&mut builder)
        };

        let mut operator = subscope.into_inner();

        operator.get_internal_summary();
        operator.set_external_summary(Vec::new(), &mut []);

        let wrapper = Wrapper {
            index: dataflow_index,
            operate: Some(Box::new(operator)),
            resources: None,
        };
        self.dataflows.borrow_mut().push(wrapper);

        result
    }

    /// Manually construct a dataflow.
    pub fn construct_dataflow<O: Operate<RootTimestamp>+'static, F: FnOnce(usize)->O>(&mut self, func: F) {
        let index = self.allocate_dataflow_index();
        let mut operator = func(index);

        // prepare progress tracking for operator.
        operator.get_internal_summary();
        operator.set_external_summary(Vec::new(), &mut []);

        let wrapper = Wrapper {
            index: index,
            operate: Some(Box::new(operator)),
            resources: None,
        };
        self.dataflows.borrow_mut().push(wrapper);
    }

    // sane way to get new dataflow identifiers; used to be self.dataflows.len(). =/
    fn allocate_dataflow_index(&mut self) -> usize {
        *self.dataflow_counter.borrow_mut() += 1;
        *self.dataflow_counter.borrow() - 1
    }
}

impl<A: Allocate> ScopeParent for Root<A> {
    type Timestamp = RootTimestamp;

    fn new_identifier(&mut self) -> usize {
        *self.identifiers.borrow_mut() += 1;
        *self.identifiers.borrow() - 1
    }
}

impl<A: Allocate> Allocate for Root<A> {
    fn index(&self) -> usize { self.allocator.borrow().index() }
    fn peers(&self) -> usize { self.allocator.borrow().peers() }
    fn allocate<D: Data>(&mut self) -> (Vec<Box<Push<D>>>, Box<Pull<D>>) {
        self.allocator.borrow_mut().allocate()
    }
}

impl<A: Allocate> Clone for Root<A> {
    fn clone(&self) -> Self {
        Root {
            allocator: self.allocator.clone(),
            identifiers: self.identifiers.clone(),
            dataflows: self.dataflows.clone(),
            dataflow_counter: self.dataflow_counter.clone(),
        }
    }
}

struct Wrapper {
    index: usize,
    operate: Option<Box<Operate<RootTimestamp>>>,
    resources: Option<Box<Drop>>,
}

impl Wrapper {
    fn step(&mut self) -> bool {
        self.operate.as_mut().map(|op| op.pull_internal_progress(&mut [], &mut [], &mut [])).unwrap_or(false)
    }
    fn active(&self) -> bool { self.operate.is_some() }
}

impl Drop for Wrapper {
    fn drop(&mut self) {
        println!("dropping dataflow {:?}", self.index);
        // ensure drop order
        self.operate = None;
        self.resources = None;
    }
}
<|endoftext|>


<commit_before>use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::hir::*;
use rustc::hir::intravisit::*;
use syntax::ast::{LitKind, NodeId, DUMMY_NODE_ID};
use syntax::codemap::{dummy_spanned, Span, DUMMY_SP};
use syntax::util::ThinVec;
use utils::{in_macro, snippet_opt, span_lint_and_then, SpanlessEq};

/// **What it does:** Checks for boolean expressions that can be written more
/// concisely.
///
/// **Why is this bad?** Readability of boolean expressions suffers from
/// unnecessary duplication.
///
/// **Known problems:** Ignores short circuiting behavior of `||` and
/// `&&`. Ignores `|`, `&` and `^`.
///
/// **Example:**
/// ```rust
/// if a && true  // should be: if a
/// if !(a == b)  // should be: if a != b
/// ```
declare_lint! {
    pub NONMINIMAL_BOOL,
    Allow,
    "boolean expressions that can be written more concisely"
}

/// **What it does:** Checks for boolean expressions that contain terminals that
/// can be eliminated.
///
/// **Why is this bad?** This is most likely a logic bug.
///
/// **Known problems:** Ignores short circuiting behavior.
///
/// **Example:**
/// ```rust
/// if a && b || a { ... }
/// ```
/// The `b` is unnecessary, the expression is equivalent to `if a`.
declare_lint! {
    pub LOGIC_BUG,
    Warn,
    "boolean expressions that contain terminals which can be eliminated"
}

const METHODS_WITH_NEGATION: [(&str, &str); 4] = [
    ("is_some", "is_none"),
    ("is_none", "is_some"),
    ("is_err", "is_ok"),
    ("is_ok", "is_err"),
];

#[derive(Copy, Clone)]
pub struct NonminimalBool;

impl LintPass for NonminimalBool {
    fn get_lints(&self) -> LintArray {
        lint_array!(NONMINIMAL_BOOL, LOGIC_BUG)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        NonminimalBoolVisitor { cx: cx }.visit_body(body)
    }
}

struct NonminimalBoolVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
}

use quine_mc_cluskey::Bool;
struct Hir2Qmm<'a, 'tcx: 'a, 'v> {
    terminals: Vec<&'v Expr>,
    cx: &'a LateContext<'a, 'tcx>,
}

impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
    fn extract(&mut self, op: BinOp_, a: &[&'v Expr], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
        for a in a {
            if let ExprBinary(binop, ref lhs, ref rhs) = a.node {
                if binop.node == op {
                    v = self.extract(op, &[lhs, rhs], v)?;
                    continue;
                }
            }
            v.push(self.run(a)?);
        }
        Ok(v)
    }

    fn run(&mut self, e: &'v Expr) -> Result<Bool, String> {
        // prevent folding of `cfg!` macros and the like
        if !in_macro(e.span) {
            match e.node {
                ExprUnary(UnNot, ref inner) => return Ok(Bool::Not(box self.run(inner)?)),
                ExprBinary(binop, ref lhs, ref rhs) => match binop.node {
                    BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
                    BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
                    _ => (),
                },
                ExprLit(ref lit) => match lit.node {
                    LitKind::Bool(true) => return Ok(Bool::True),
                    LitKind::Bool(false) => return Ok(Bool::False),
                    _ => (),
                },
                _ => (),
            }
        }
        for (n, expr) in self.terminals.iter().enumerate() {
            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
                #[allow(cast_possible_truncation)]
                return Ok(Bool::Term(n as u8));
            }
            let negated = match e.node {
                ExprBinary(binop, ref lhs, ref rhs) => {
                    let mk_expr = |op| {
                        Expr {
                            id: DUMMY_NODE_ID,
                            hir_id: DUMMY_HIR_ID,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                            node: ExprBinary(dummy_spanned(op), lhs.clone(), rhs.clone()),
                        }
                    };
                    match binop.node {
                        BiEq => mk_expr(BiNe),
                        BiNe => mk_expr(BiEq),
                        BiGt => mk_expr(BiLe),
                        BiGe => mk_expr(BiLt),
                        BiLt => mk_expr(BiGe),
                        BiLe => mk_expr(BiGt),
                        _ => continue,
                    }
                },
                _ => continue,
            };
            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(&negated, expr) {
                #[allow(cast_possible_truncation)]
                return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
            }
        }
        let n = self.terminals.len();
        self.terminals.push(e);
        if n < 32 {
            #[allow(cast_possible_truncation)]
            Ok(Bool::Term(n as u8))
        } else {
            Err("too many literals".to_owned())
        }
    }
}

fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
    fn recurse(brackets: bool, cx: &LateContext, suggestion: &Bool, terminals: &[&Expr], mut s: String) -> String {
        use quine_mc_cluskey::Bool::*;
        let snip = |e: &Expr| snippet_opt(cx, e.span).expect("don't try to improve booleans created by macros");
        match *suggestion {
            True => {
                s.push_str("true");
                s
            },
            False => {
                s.push_str("false");
                s
            },
            Not(ref inner) => match **inner {
                And(_) | Or(_) => {
                    s.push('!');
                    recurse(true, cx, inner, terminals, s)
                },
                Term(n) => if let ExprBinary(binop, ref lhs, ref rhs) = terminals[n as usize].node {
                    let op = match binop.node {
                        BiEq => " != ",
                        BiNe => " == ",
                        BiLt => " >= ",
                        BiGt => " <= ",
                        BiLe => " > ",
                        BiGe => " < ",
                        _ => {
                            s.push('!');
                            return recurse(true, cx, inner, terminals, s);
                        },
                    };
                    s.push_str(&snip(lhs));
                    s.push_str(op);
                    s.push_str(&snip(rhs));
                    s
                } else {
                    s.push('!');
                    recurse(false, cx, inner, terminals, s)
                },
                _ => {
                    s.push('!');
                    recurse(false, cx, inner, terminals, s)
                },
            },
            And(ref v) => {
                if brackets {
                    s.push('(');
                }
                if let Or(_) = v[0] {
                    s = recurse(true, cx, &v[0], terminals, s);
                } else {
                    s = recurse(false, cx, &v[0], terminals, s);
                }
                for inner in &v[1..] {
                    s.push_str(" && ");
                    if let Or(_) = *inner {
                        s = recurse(true, cx, inner, terminals, s);
                    } else {
                        s = recurse(false, cx, inner, terminals, s);
                    }
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Or(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(false, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" || ");
                    s = recurse(false, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Term(n) => {
                if brackets {
                    if let ExprBinary(..) = terminals[n as usize].node {
                        s.push('(');
                    }
                }
                s.push_str(&snip(terminals[n as usize]));
                if brackets {
                    if let ExprBinary(..) = terminals[n as usize].node {
                        s.push(')');
                    }
                }
                s
            },
        }
    }
    recurse(false, cx, suggestion, terminals, String::new())
}

fn simple_negate(b: Bool) -> Bool {
    use quine_mc_cluskey::Bool::*;
    match b {
        True => False,
        False => True,
        t @ Term(_) => Not(Box::new(t)),
        And(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            Or(v)
        },
        Or(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            And(v)
        },
        Not(inner) => *inner,
    }
}

#[derive(Default)]
struct Stats {
    terminals: [usize; 32],
    negations: usize,
    ops: usize,
}

fn terminal_stats(b: &Bool) -> Stats {
    fn recurse(b: &Bool, stats: &mut Stats) {
        match *b {
            True | False => stats.ops += 1,
            Not(ref inner) => {
                match **inner {
                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
                    _ => stats.negations += 1,
                }
                recurse(inner, stats);
            },
            And(ref v) | Or(ref v) => {
                stats.ops += v.len() - 1;
                for inner in v {
                    recurse(inner, stats);
                }
            },
            Term(n) => stats.terminals[n as usize] += 1,
        }
    }
    use quine_mc_cluskey::Bool::*;
    let mut stats = Stats::default();
    recurse(b, &mut stats);
    stats
}

impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
    fn bool_expr(&self, e: &Expr) {
        let mut h2q = Hir2Qmm {
            terminals: Vec::new(),
            cx: self.cx,
        };
        if let Ok(expr) = h2q.run(e) {
            if h2q.terminals.len() > 8 {
                // QMC has exponentially slow behavior as the number of terminals increases
                // 8 is reasonable, it takes approximately 0.2 seconds.
                // See #825
                return;
            }

            let stats = terminal_stats(&expr);
            let mut simplified = expr.simplify();
            for simple in Bool::Not(Box::new(expr.clone())).simplify() {
                match simple {
                    Bool::Not(_) | Bool::True | Bool::False => {},
                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
                }
                let simple_negated = simple_negate(simple);
                if simplified.iter().any(|s| *s == simple_negated) {
                    continue;
                }
                simplified.push(simple_negated);
            }
            let mut improvements = Vec::new();
            'simplified: for suggestion in &simplified {
                let simplified_stats = terminal_stats(suggestion);
                let mut improvement = false;
                for i in 0..32 {
                    // ignore any "simplifications" that end up requiring a terminal more often
                    // than in the original expression
                    if stats.terminals[i] < simplified_stats.terminals[i] {
                        continue 'simplified;
                    }
                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
                        span_lint_and_then(
                            self.cx,
                            LOGIC_BUG,
                            e.span,
                            "this boolean expression contains a logic bug",
                            |db| {
                                db.span_help(
                                    h2q.terminals[i].span,
                                    "this expression can be optimized out by applying boolean operations to the \
                                     outer expression",
                                );
                                db.span_suggestion(
                                    e.span,
                                    "it would look like the following",
                                    suggest(self.cx, suggestion, &h2q.terminals),
                                );
                            },
                        );
                        // don't also lint `NONMINIMAL_BOOL`
                        return;
                    }
                    // if the number of occurrences of a terminal decreases or any of the stats
                    // decreases while none increases
                    improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
                        || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
                        || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
                }
                if improvement {
                    improvements.push(suggestion);
                }
            }
            if !improvements.is_empty() {
                span_lint_and_then(
                    self.cx,
                    NONMINIMAL_BOOL,
                    e.span,
                    "this boolean expression can be simplified",
                    |db| {
                        db.span_suggestions(
                            e.span,
                            "try",
                            improvements
                                .into_iter()
                                .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
                                .collect(),
                        );
                    },
                );
            }
        }
    }

    fn handle_method_call_in_not(&mut self, e: &'tcx Expr, inner: &'tcx Expr) {
        if let ExprMethodCall(ref path, _, _) = inner.node {
            METHODS_WITH_NEGATION.iter().for_each(|&(method, negation_method)| {
                if method == path.name.as_str() {
                    span_lint_and_then(
                        self.cx,
                        NONMINIMAL_BOOL,
                        e.span,
                        "this boolean expression can be simplified",
                        |db| {
                            db.span_suggestion(
                                e.span,
                                "try",
                                negation_method.to_owned()
                            );
                        }
                    )
                }
            })
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, e: &'tcx Expr) {
        if in_macro(e.span) {
            return;
        }
        match e.node {
            ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
            ExprUnary(UnNot, ref inner) => if self.cx.tables.node_types()[inner.hir_id].is_bool() {
                self.handle_method_call_in_not(e, inner);
                self.bool_expr(e);
            } else {
                walk_expr(self, e);
            },
            _ => walk_expr(self, e),
        }
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<commit_msg>Only apply when there is a single argument.<commit_after>use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::hir::*;
use rustc::hir::intravisit::*;
use syntax::ast::{LitKind, NodeId, DUMMY_NODE_ID};
use syntax::codemap::{dummy_spanned, Span, DUMMY_SP};
use syntax::util::ThinVec;
use utils::{in_macro, snippet_opt, span_lint_and_then, SpanlessEq};

/// **What it does:** Checks for boolean expressions that can be written more
/// concisely.
///
/// **Why is this bad?** Readability of boolean expressions suffers from
/// unnecessary duplication.
///
/// **Known problems:** Ignores short circuiting behavior of `||` and
/// `&&`. Ignores `|`, `&` and `^`.
///
/// **Example:**
/// ```rust
/// if a && true  // should be: if a
/// if !(a == b)  // should be: if a != b
/// ```
declare_lint! {
    pub NONMINIMAL_BOOL,
    Allow,
    "boolean expressions that can be written more concisely"
}

/// **What it does:** Checks for boolean expressions that contain terminals that
/// can be eliminated.
///
/// **Why is this bad?** This is most likely a logic bug.
///
/// **Known problems:** Ignores short circuiting behavior.
///
/// **Example:**
/// ```rust
/// if a && b || a { ... }
/// ```
/// The `b` is unnecessary, the expression is equivalent to `if a`.
declare_lint! {
    pub LOGIC_BUG,
    Warn,
    "boolean expressions that contain terminals which can be eliminated"
}

const METHODS_WITH_NEGATION: [(&str, &str); 4] = [
    ("is_some", "is_none"),
    ("is_none", "is_some"),
    ("is_err", "is_ok"),
    ("is_ok", "is_err"),
];

#[derive(Copy, Clone)]
pub struct NonminimalBool;

impl LintPass for NonminimalBool {
    fn get_lints(&self) -> LintArray {
        lint_array!(NONMINIMAL_BOOL, LOGIC_BUG)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: NodeId,
    ) {
        NonminimalBoolVisitor { cx: cx }.visit_body(body)
    }
}

struct NonminimalBoolVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
}

use quine_mc_cluskey::Bool;
struct Hir2Qmm<'a, 'tcx: 'a, 'v> {
    terminals: Vec<&'v Expr>,
    cx: &'a LateContext<'a, 'tcx>,
}

impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
    fn extract(&mut self, op: BinOp_, a: &[&'v Expr], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
        for a in a {
            if let ExprBinary(binop, ref lhs, ref rhs) = a.node {
                if binop.node == op {
                    v = self.extract(op, &[lhs, rhs], v)?;
                    continue;
                }
            }
            v.push(self.run(a)?);
        }
        Ok(v)
    }

    fn run(&mut self, e: &'v Expr) -> Result<Bool, String> {
        // prevent folding of `cfg!` macros and the like
        if !in_macro(e.span) {
            match e.node {
                ExprUnary(UnNot, ref inner) => return Ok(Bool::Not(box self.run(inner)?)),
                ExprBinary(binop, ref lhs, ref rhs) => match binop.node {
                    BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
                    BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
                    _ => (),
                },
                ExprLit(ref lit) => match lit.node {
                    LitKind::Bool(true) => return Ok(Bool::True),
                    LitKind::Bool(false) => return Ok(Bool::False),
                    _ => (),
                },
                _ => (),
            }
        }
        for (n, expr) in self.terminals.iter().enumerate() {
            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
                #[allow(cast_possible_truncation)]
                return Ok(Bool::Term(n as u8));
            }
            let negated = match e.node {
                ExprBinary(binop, ref lhs, ref rhs) => {
                    let mk_expr = |op| {
                        Expr {
                            id: DUMMY_NODE_ID,
                            hir_id: DUMMY_HIR_ID,
                            span: DUMMY_SP,
                            attrs: ThinVec::new(),
                            node: ExprBinary(dummy_spanned(op), lhs.clone(), rhs.clone()),
                        }
                    };
                    match binop.node {
                        BiEq => mk_expr(BiNe),
                        BiNe => mk_expr(BiEq),
                        BiGt => mk_expr(BiLe),
                        BiGe => mk_expr(BiLt),
                        BiLt => mk_expr(BiGe),
                        BiLe => mk_expr(BiGt),
                        _ => continue,
                    }
                },
                _ => continue,
            };
            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(&negated, expr) {
                #[allow(cast_possible_truncation)]
                return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
            }
        }
        let n = self.terminals.len();
        self.terminals.push(e);
        if n < 32 {
            #[allow(cast_possible_truncation)]
            Ok(Bool::Term(n as u8))
        } else {
            Err("too many literals".to_owned())
        }
    }
}

fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
    fn recurse(brackets: bool, cx: &LateContext, suggestion: &Bool, terminals: &[&Expr], mut s: String) -> String {
        use quine_mc_cluskey::Bool::*;
        let snip = |e: &Expr| snippet_opt(cx, e.span).expect("don't try to improve booleans created by macros");
        match *suggestion {
            True => {
                s.push_str("true");
                s
            },
            False => {
                s.push_str("false");
                s
            },
            Not(ref inner) => match **inner {
                And(_) | Or(_) => {
                    s.push('!');
                    recurse(true, cx, inner, terminals, s)
                },
                Term(n) => if let ExprBinary(binop, ref lhs, ref rhs) = terminals[n as usize].node {
                    let op = match binop.node {
                        BiEq => " != ",
                        BiNe => " == ",
                        BiLt => " >= ",
                        BiGt => " <= ",
                        BiLe => " > ",
                        BiGe => " < ",
                        _ => {
                            s.push('!');
                            return recurse(true, cx, inner, terminals, s);
                        },
                    };
                    s.push_str(&snip(lhs));
                    s.push_str(op);
                    s.push_str(&snip(rhs));
                    s
                } else {
                    s.push('!');
                    recurse(false, cx, inner, terminals, s)
                },
                _ => {
                    s.push('!');
                    recurse(false, cx, inner, terminals, s)
                },
            },
            And(ref v) => {
                if brackets {
                    s.push('(');
                }
                if let Or(_) = v[0] {
                    s = recurse(true, cx, &v[0], terminals, s);
                } else {
                    s = recurse(false, cx, &v[0], terminals, s);
                }
                for inner in &v[1..] {
                    s.push_str(" && ");
                    if let Or(_) = *inner {
                        s = recurse(true, cx, inner, terminals, s);
                    } else {
                        s = recurse(false, cx, inner, terminals, s);
                    }
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Or(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(false, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" || ");
                    s = recurse(false, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Term(n) => {
                if brackets {
                    if let ExprBinary(..) = terminals[n as usize].node {
                        s.push('(');
                    }
                }
                s.push_str(&snip(terminals[n as usize]));
                if brackets {
                    if let ExprBinary(..) = terminals[n as usize].node {
                        s.push(')');
                    }
                }
                s
            },
        }
    }
    recurse(false, cx, suggestion, terminals, String::new())
}

fn simple_negate(b: Bool) -> Bool {
    use quine_mc_cluskey::Bool::*;
    match b {
        True => False,
        False => True,
        t @ Term(_) => Not(Box::new(t)),
        And(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            Or(v)
        },
        Or(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            And(v)
        },
        Not(inner) => *inner,
    }
}

#[derive(Default)]
struct Stats {
    terminals: [usize; 32],
    negations: usize,
    ops: usize,
}

fn terminal_stats(b: &Bool) -> Stats {
    fn recurse(b: &Bool, stats: &mut Stats) {
        match *b {
            True | False => stats.ops += 1,
            Not(ref inner) => {
                match **inner {
                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
                    _ => stats.negations += 1,
                }
                recurse(inner, stats);
            },
            And(ref v) | Or(ref v) => {
                stats.ops += v.len() - 1;
                for inner in v {
                    recurse(inner, stats);
                }
            },
            Term(n) => stats.terminals[n as usize] += 1,
        }
    }
    use quine_mc_cluskey::Bool::*;
    let mut stats = Stats::default();
    recurse(b, &mut stats);
    stats
}

impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
    fn bool_expr(&self, e: &Expr) {
        let mut h2q = Hir2Qmm {
            terminals: Vec::new(),
            cx: self.cx,
        };
        if let Ok(expr) = h2q.run(e) {
            if h2q.terminals.len() > 8 {
                // QMC has exponentially slow behavior as the number of terminals increases
                // 8 is reasonable, it takes approximately 0.2 seconds.
                // See #825
                return;
            }

            let stats = terminal_stats(&expr);
            let mut simplified = expr.simplify();
            for simple in Bool::Not(Box::new(expr.clone())).simplify() {
                match simple {
                    Bool::Not(_) | Bool::True | Bool::False => {},
                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
                }
                let simple_negated = simple_negate(simple);
                if simplified.iter().any(|s| *s == simple_negated) {
                    continue;
                }
                simplified.push(simple_negated);
            }
            let mut improvements = Vec::new();
            'simplified: for suggestion in &simplified {
                let simplified_stats = terminal_stats(suggestion);
                let mut improvement = false;
                for i in 0..32 {
                    // ignore any "simplifications" that end up requiring a terminal more often
                    // than in the original expression
                    if stats.terminals[i] < simplified_stats.terminals[i] {
                        continue 'simplified;
                    }
                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
                        span_lint_and_then(
                            self.cx,
                            LOGIC_BUG,
                            e.span,
                            "this boolean expression contains a logic bug",
                            |db| {
                                db.span_help(
                                    h2q.terminals[i].span,
                                    "this expression can be optimized out by applying boolean operations to the \
                                     outer expression",
                                );
                                db.span_suggestion(
                                    e.span,
                                    "it would look like the following",
                                    suggest(self.cx, suggestion, &h2q.terminals),
                                );
                            },
                        );
                        // don't also lint `NONMINIMAL_BOOL`
                        return;
                    }
                    // if the number of occurrences of a terminal decreases or any of the stats
                    // decreases while none increases
                    improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
                        || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
                        || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
                }
                if improvement {
                    improvements.push(suggestion);
                }
            }
            if !improvements.is_empty() {
                span_lint_and_then(
                    self.cx,
                    NONMINIMAL_BOOL,
                    e.span,
                    "this boolean expression can be simplified",
                    |db| {
                        db.span_suggestions(
                            e.span,
                            "try",
                            improvements
                                .into_iter()
                                .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
                                .collect(),
                        );
                    },
                );
            }
        }
    }

    fn handle_method_call_in_not(&mut self, e: &'tcx Expr, inner: &'tcx Expr) {
        if let ExprMethodCall(ref path, _, ref args) = inner.node {
            if args.len() == 1 {
                METHODS_WITH_NEGATION.iter().for_each(|&(method, negation_method)| {
                    if method == path.name.as_str() {
                        span_lint_and_then(
                            self.cx,
                            NONMINIMAL_BOOL,
                            e.span,
                            "this boolean expression can be simplified",
                            |db| {
                                db.span_suggestion(
                                    e.span,
                                    "try",
                                    negation_method.to_owned()
                                );
                            }
                        )
                    }
                })
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, e: &'tcx Expr) {
        if in_macro(e.span) {
            return;
        }
        match e.node {
            ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
            ExprUnary(UnNot, ref inner) => if self.cx.tables.node_types()[inner.hir_id].is_bool() {
                self.handle_method_call_in_not(e, inner);
                self.bool_expr(e);
            } else {
                walk_expr(self, e);
            },
            _ => walk_expr(self, e),
        }
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<|endoftext|>


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

//! Contains types which encode the results of actions requested on an engine,
//! pool, filesystem, or blockdev. Each action type is designed to support
//! idempotency. In every case, the action type is used to indicate the
//! effect of the action at the time the action is requested. The action was
//! completed succesfully; this type indicates what changes had to be made.

use std::fmt::{self, Display};

use crate::engine::{
    engine::Filesystem,
    types::{DevUuid, FilesystemUuid, PoolUuid},
};

/// Return value indicating key operation
pub struct Key;

/// Return value indicating clevis operation
pub struct Clevis;

/// A trait for a generic kind of action. Defines the type of the thing to
/// be changed, and also a method to indicate what changed.
pub trait EngineAction {
    type Return;

    /// Returns whether or not the action changed state.
    fn is_changed(&self) -> bool;

    /// Returns the thing or things changed.
    fn changed(self) -> Option<Self::Return>;
}

#[derive(Debug, PartialEq, Eq)]
/// A single create action.
pub enum CreateAction<T> {
    /// The thing already existed.
    Identity,
    /// The thing did not already exist.
    Created(T),
}

impl<T> EngineAction for CreateAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        !matches!(*self, CreateAction::Identity)
    }

    fn changed(self) -> Option<T> {
        match self {
            CreateAction::Created(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for CreateAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created(uuid) => {
                write!(f, "Pool with UUID {} was created successfully", uuid)
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The pool requested for creation is already present; no action taken"
                )
            }
        }
    }
}

impl Display for CreateAction<Clevis> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created(Clevis) => {
                write!(
                    f,
                    "Pool successfully bound to an unlocking mechanism using clevis"
                )
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The pool requested for binding is already bound; no action taken"
                )
            }
        }
    }
}

impl Display for CreateAction<(FilesystemUuid, &mut dyn Filesystem)> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created((uuid, fs)) => {
                write!(
                    f,
                    "Snapshot with UUID {} was created successfully from filesystem with path {}",
                    uuid,
                    fs.devnode().display()
                )
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The snapshot requested for creation is already present; no action taken"
                )
            }
        }
    }
}

/// Idempotent type representing a create action for a mapping from a key to a value
#[derive(Debug, PartialEq, Eq)]
pub enum MappingCreateAction<T> {
    /// The key did not exist and the key and value are newly created.
    Created(T),
    /// The key and the value were not changed.
    Identity,
    /// The key existed and the value was updated.
    ValueChanged(T),
}

impl<T> EngineAction for MappingCreateAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(*self, MappingCreateAction::Created(_) | MappingCreateAction::ValueChanged(_))
    }

    fn changed(self) -> Option<T> {
        match self {
            MappingCreateAction::Created(t) | MappingCreateAction::ValueChanged(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for MappingCreateAction<Key> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MappingCreateAction::Created(Key) => write!(f, "Set a new key successfully"),
            MappingCreateAction::Identity => {
                write!(f, "The requested key already exists; no action was taken")
            }
            MappingCreateAction::ValueChanged(Key) => write!(
                f,
                "An existing key was updated with a new value successfully"
            ),
        }
    }
}

/// A type for the return type of idempotent unlocking actions.
pub struct SetUnlockAction<T> {
    unlocked: Vec<T>,
}

impl<T> SetUnlockAction<T> {
    /// Create a new return type with newly unlocked resources and resources that
    /// are still locked.
    pub fn new(unlocked: Vec<T>) -> SetUnlockAction<T> {
        SetUnlockAction { unlocked }
    }

    /// Create a new return type where no newly unlocked resources are reported.
    pub fn empty() -> SetUnlockAction<T> {
        SetUnlockAction {
            unlocked: Vec::new(),
        }
    }
}

impl<T> EngineAction for SetUnlockAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.unlocked.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.unlocked.is_empty() {
            None
        } else {
            Some(self.unlocked)
        }
    }
}

impl Display for SetUnlockAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.unlocked.is_empty() {
            write!(
                f,
                "No new devices were able to be unlocked; no action was taken"
            )
        } else {
            write!(
                f,
                "The devices with UUIDs {} were successfully unlocked",
                self.unlocked
                    .iter()
                    .map(|uuid| uuid.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// An action which may create multiple things.
pub struct SetCreateAction<T> {
    changed: Vec<T>,
}

impl<T> SetCreateAction<T> {
    pub fn new(changed: Vec<T>) -> Self {
        SetCreateAction { changed }
    }

    pub fn empty() -> Self {
        SetCreateAction { changed: vec![] }
    }
}

impl<T> EngineAction for SetCreateAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.changed.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.changed.is_empty() {
            None
        } else {
            Some(self.changed)
        }
    }
}

impl Display for SetCreateAction<(&str, FilesystemUuid)> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems already exist; no action taken"
            )
        } else {
            write!(
                f,
                "The following filesystems {} were successfully created",
                self.changed
                    .iter()
                    .map(|(n, u)| format!("name: {}, UUID: {}", n, u))
                    .collect::<Vec<_>>()
                    .join("; ")
            )
        }
    }
}

impl Display for SetCreateAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems already exist; no action taken"
            )
        } else {
            write!(
                f,
                "The following devices with UUIDs {} were successfully added to a pool",
                self.changed
                    .iter()
                    .map(|u| u.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// An action which may rename a single thing.
pub enum RenameAction<T> {
    /// The thing already had the given name.
    Identity,
    /// The thing did not have the given name and was renamed.
    Renamed(T),
    /// The thing did not exist, so could not be renamed.
    NoSource,
}

impl<T> EngineAction for RenameAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(*self, RenameAction::Renamed(_))
    }

    fn changed(self) -> Option<T> {
        match self {
            RenameAction::Renamed(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for RenameAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(
                    f,
                    "Device is already named the target name; no action taken"
                )
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Device with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The device requested to be renamed does not exist")
            }
        }
    }
}

impl Display for RenameAction<FilesystemUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(
                    f,
                    "Filesystem is already named the target name; no action taken"
                )
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Filesystem with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The filesystem requested to be renamed does not exist")
            }
        }
    }
}

impl Display for RenameAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(f, "Pool is already named the target name; no action taken")
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Pool with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The pool requested to be renamed does not exist")
            }
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// A single delete action.
pub enum DeleteAction<T> {
    /// The thing was already gone.
    Identity,
    /// The thing existed and was removed.
    Deleted(T),
}

impl<T> EngineAction for DeleteAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(*self, DeleteAction::Deleted(_))
    }

    fn changed(self) -> Option<T> {
        match self {
            DeleteAction::Deleted(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for DeleteAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(uuid) => {
                write!(f, "Pool with UUID {} was deleted successfully", uuid)
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The pool requested for deletion is already absent; no action taken"
                )
            }
        }
    }
}

impl Display for DeleteAction<Clevis> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(_) => {
                write!(f, "A clevis binding was successfully removed from a pool")
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The clevis binding requested for removal is already absent; no action taken"
                )
            }
        }
    }
}

impl Display for DeleteAction<Key> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(_) => {
                write!(f, "A key was deleted successfully")
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The key requested for deletion is already absent; no action taken"
                )
            }
        }
    }
}

/// An action which may create multiple things.
#[derive(Debug, PartialEq, Eq)]
pub struct SetDeleteAction<T> {
    changed: Vec<T>,
}

impl<T> SetDeleteAction<T> {
    pub fn new(changed: Vec<T>) -> Self {
        SetDeleteAction { changed }
    }

    pub fn empty() -> Self {
        SetDeleteAction { changed: vec![] }
    }
}

impl<T> EngineAction for SetDeleteAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.changed.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.changed.is_empty() {
            None
        } else {
            Some(self.changed)
        }
    }
}

impl Display for SetDeleteAction<FilesystemUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems are already absent; no action taken"
            )
        } else {
            write!(
                f,
                "Filesystems with UUIDs {} were successfully deleted",
                self.changed
                    .iter()
                    .map(|u| u.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}
<commit_msg>Wrap matches line for is_changed()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Contains types which encode the results of actions requested on an engine,
//! pool, filesystem, or blockdev. Each action type is designed to support
//! idempotency. In every case, the action type is used to indicate the
//! effect of the action at the time the action is requested. The action was
//! completed succesfully; this type indicates what changes had to be made.

use std::fmt::{self, Display};

use crate::engine::{
    engine::Filesystem,
    types::{DevUuid, FilesystemUuid, PoolUuid},
};

/// Return value indicating key operation
pub struct Key;

/// Return value indicating clevis operation
pub struct Clevis;

/// A trait for a generic kind of action. Defines the type of the thing to
/// be changed, and also a method to indicate what changed.
pub trait EngineAction {
    type Return;

    /// Returns whether or not the action changed state.
    fn is_changed(&self) -> bool;

    /// Returns the thing or things changed.
    fn changed(self) -> Option<Self::Return>;
}

#[derive(Debug, PartialEq, Eq)]
/// A single create action.
pub enum CreateAction<T> {
    /// The thing already existed.
    Identity,
    /// The thing did not already exist.
    Created(T),
}

impl<T> EngineAction for CreateAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        !matches!(*self, CreateAction::Identity)
    }

    fn changed(self) -> Option<T> {
        match self {
            CreateAction::Created(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for CreateAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created(uuid) => {
                write!(f, "Pool with UUID {} was created successfully", uuid)
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The pool requested for creation is already present; no action taken"
                )
            }
        }
    }
}

impl Display for CreateAction<Clevis> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created(Clevis) => {
                write!(
                    f,
                    "Pool successfully bound to an unlocking mechanism using clevis"
                )
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The pool requested for binding is already bound; no action taken"
                )
            }
        }
    }
}

impl Display for CreateAction<(FilesystemUuid, &mut dyn Filesystem)> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CreateAction::Created((uuid, fs)) => {
                write!(
                    f,
                    "Snapshot with UUID {} was created successfully from filesystem with path {}",
                    uuid,
                    fs.devnode().display()
                )
            }
            CreateAction::Identity => {
                write!(
                    f,
                    "The snapshot requested for creation is already present; no action taken"
                )
            }
        }
    }
}

/// Idempotent type representing a create action for a mapping from a key to a value
#[derive(Debug, PartialEq, Eq)]
pub enum MappingCreateAction<T> {
    /// The key did not exist and the key and value are newly created.
    Created(T),
    /// The key and the value were not changed.
    Identity,
    /// The key existed and the value was updated.
    ValueChanged(T),
}

impl<T> EngineAction for MappingCreateAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(
            *self,
            MappingCreateAction::Created(_) | MappingCreateAction::ValueChanged(_)
        )
    }

    fn changed(self) -> Option<T> {
        match self {
            MappingCreateAction::Created(t) | MappingCreateAction::ValueChanged(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for MappingCreateAction<Key> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            MappingCreateAction::Created(Key) => write!(f, "Set a new key successfully"),
            MappingCreateAction::Identity => {
                write!(f, "The requested key already exists; no action was taken")
            }
            MappingCreateAction::ValueChanged(Key) => write!(
                f,
                "An existing key was updated with a new value successfully"
            ),
        }
    }
}

/// A type for the return type of idempotent unlocking actions.
pub struct SetUnlockAction<T> {
    unlocked: Vec<T>,
}

impl<T> SetUnlockAction<T> {
    /// Create a new return type with newly unlocked resources and resources that
    /// are still locked.
    pub fn new(unlocked: Vec<T>) -> SetUnlockAction<T> {
        SetUnlockAction { unlocked }
    }

    /// Create a new return type where no newly unlocked resources are reported.
    pub fn empty() -> SetUnlockAction<T> {
        SetUnlockAction {
            unlocked: Vec::new(),
        }
    }
}

impl<T> EngineAction for SetUnlockAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.unlocked.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.unlocked.is_empty() {
            None
        } else {
            Some(self.unlocked)
        }
    }
}

impl Display for SetUnlockAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.unlocked.is_empty() {
            write!(
                f,
                "No new devices were able to be unlocked; no action was taken"
            )
        } else {
            write!(
                f,
                "The devices with UUIDs {} were successfully unlocked",
                self.unlocked
                    .iter()
                    .map(|uuid| uuid.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// An action which may create multiple things.
pub struct SetCreateAction<T> {
    changed: Vec<T>,
}

impl<T> SetCreateAction<T> {
    pub fn new(changed: Vec<T>) -> Self {
        SetCreateAction { changed }
    }

    pub fn empty() -> Self {
        SetCreateAction { changed: vec![] }
    }
}

impl<T> EngineAction for SetCreateAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.changed.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.changed.is_empty() {
            None
        } else {
            Some(self.changed)
        }
    }
}

impl Display for SetCreateAction<(&str, FilesystemUuid)> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems already exist; no action taken"
            )
        } else {
            write!(
                f,
                "The following filesystems {} were successfully created",
                self.changed
                    .iter()
                    .map(|(n, u)| format!("name: {}, UUID: {}", n, u))
                    .collect::<Vec<_>>()
                    .join("; ")
            )
        }
    }
}

impl Display for SetCreateAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems already exist; no action taken"
            )
        } else {
            write!(
                f,
                "The following devices with UUIDs {} were successfully added to a pool",
                self.changed
                    .iter()
                    .map(|u| u.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// An action which may rename a single thing.
pub enum RenameAction<T> {
    /// The thing already had the given name.
    Identity,
    /// The thing did not have the given name and was renamed.
    Renamed(T),
    /// The thing did not exist, so could not be renamed.
    NoSource,
}

impl<T> EngineAction for RenameAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(*self, RenameAction::Renamed(_))
    }

    fn changed(self) -> Option<T> {
        match self {
            RenameAction::Renamed(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for RenameAction<DevUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(
                    f,
                    "Device is already named the target name; no action taken"
                )
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Device with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The device requested to be renamed does not exist")
            }
        }
    }
}

impl Display for RenameAction<FilesystemUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(
                    f,
                    "Filesystem is already named the target name; no action taken"
                )
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Filesystem with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The filesystem requested to be renamed does not exist")
            }
        }
    }
}

impl Display for RenameAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RenameAction::Identity => {
                write!(f, "Pool is already named the target name; no action taken")
            }
            RenameAction::Renamed(uuid) => {
                write!(f, "Pool with UUID {} was successfully renamed", uuid)
            }
            RenameAction::NoSource => {
                write!(f, "The pool requested to be renamed does not exist")
            }
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
/// A single delete action.
pub enum DeleteAction<T> {
    /// The thing was already gone.
    Identity,
    /// The thing existed and was removed.
    Deleted(T),
}

impl<T> EngineAction for DeleteAction<T> {
    type Return = T;

    fn is_changed(&self) -> bool {
        matches!(*self, DeleteAction::Deleted(_))
    }

    fn changed(self) -> Option<T> {
        match self {
            DeleteAction::Deleted(t) => Some(t),
            _ => None,
        }
    }
}

impl Display for DeleteAction<PoolUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(uuid) => {
                write!(f, "Pool with UUID {} was deleted successfully", uuid)
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The pool requested for deletion is already absent; no action taken"
                )
            }
        }
    }
}

impl Display for DeleteAction<Clevis> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(_) => {
                write!(f, "A clevis binding was successfully removed from a pool")
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The clevis binding requested for removal is already absent; no action taken"
                )
            }
        }
    }
}

impl Display for DeleteAction<Key> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DeleteAction::Deleted(_) => {
                write!(f, "A key was deleted successfully")
            }
            DeleteAction::Identity => {
                write!(
                    f,
                    "The key requested for deletion is already absent; no action taken"
                )
            }
        }
    }
}

/// An action which may create multiple things.
#[derive(Debug, PartialEq, Eq)]
pub struct SetDeleteAction<T> {
    changed: Vec<T>,
}

impl<T> SetDeleteAction<T> {
    pub fn new(changed: Vec<T>) -> Self {
        SetDeleteAction { changed }
    }

    pub fn empty() -> Self {
        SetDeleteAction { changed: vec![] }
    }
}

impl<T> EngineAction for SetDeleteAction<T> {
    type Return = Vec<T>;

    fn is_changed(&self) -> bool {
        !self.changed.is_empty()
    }

    fn changed(self) -> Option<Vec<T>> {
        if self.changed.is_empty() {
            None
        } else {
            Some(self.changed)
        }
    }
}

impl Display for SetDeleteAction<FilesystemUuid> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.changed.is_empty() {
            write!(
                f,
                "The requested filesystems are already absent; no action taken"
            )
        } else {
            write!(
                f,
                "Filesystems with UUIDs {} were successfully deleted",
                self.changed
                    .iter()
                    .map(|u| u.to_string())
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}
<|endoftext|>


<commit_before>//! The [glyph-definition table][1].
//!
//! [1]: https://www.microsoft.com/typography/otspec/GDEF.htm

use truetype::{Result, Tape, Value};

use layout::Class;

mod element;

pub use self::element::*;

table! {
    @position
    #[doc = "A glyph-definition table."]
    pub GlyphDefinition {
        header (Header),

        glyph_class (Option<Class>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.glyph_class_offset,
                Header::Version12(ref header) => header.glyph_class_offset,
            })
        },

        attachments (Option<Attachments>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.attachments_offset,
                Header::Version12(ref header) => header.attachments_offset,
            })
        },

        ligatures (Option<Ligatures>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.ligatures_offset,
                Header::Version12(ref header) => header.ligatures_offset,
            })
        },

        mark_class (Option<Class>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.mark_class_offset,
                Header::Version12(ref header) => header.mark_class_offset,
            })
        },

        marks (Option<Marks>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version12(ref header) => header.marks_offset,
                _ => 0,
            })
        },
    }
}

/// The header of a glyph-definition table.
#[derive(Clone, Debug)]
pub enum Header {
    /// Version 1.0.
    Version1(Header1),
    /// Version 1.2.
    Version12(Header12),
}

table! {
    #[doc = "The header of a glyph-definition table of version 1.0."]
    #[derive(Copy)]
    pub Header1 {
        major_version      (u16) = { 1 }, // MajorVersion
        minor_version      (u16) = { 0 }, // MinorVersion
        glyph_class_offset (u16), // GlyphClassDef
        attachments_offset (u16), // AttachList
        ligatures_offset   (u16), // LigCaretList
        mark_class_offset  (u16), // MarkAttachClassDef
    }
}

table! {
    #[doc = "The header of a glyph-definition table of version 1.2."]
    #[derive(Copy)]
    pub Header12 {
        major_version      (u16) = { 1 }, // MajorVersion
        minor_version      (u16) = { 2 }, // MinorVersion
        glyph_class_offset (u16), // GlyphClassDef
        attachments_offset (u16), // AttachList
        ligatures_offset   (u16), // LigCaretList
        mark_class_offset  (u16), // MarkAttachClassDef
        marks_offset       (u16), // MarkGlyphSetsDef
    }
}

impl Value for Header {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u32>()) {
            0x00010000 => Header::Version1(try!(tape.take())),
            0x00010002 => Header::Version12(try!(tape.take())),
            _ => raise!("found an unknown format of the glyph-definition table"),
        })
    }
}
<commit_msg>Parse the GDEF header of version 1.3<commit_after>//! The [glyph-definition table][1].
//!
//! [1]: https://www.microsoft.com/typography/otspec/GDEF.htm

use truetype::{Result, Tape, Value};

use layout::Class;

mod element;

pub use self::element::*;

table! {
    @position
    #[doc = "A glyph-definition table."]
    pub GlyphDefinition {
        header (Header),

        glyph_class (Option<Class>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.glyph_class_offset,
                Header::Version12(ref header) => header.glyph_class_offset,
                Header::Version13(ref header) => header.glyph_class_offset,
            })
        },

        attachments (Option<Attachments>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.attachments_offset,
                Header::Version12(ref header) => header.attachments_offset,
                Header::Version13(ref header) => header.attachments_offset,
            })
        },

        ligatures (Option<Ligatures>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.ligatures_offset,
                Header::Version12(ref header) => header.ligatures_offset,
                Header::Version13(ref header) => header.ligatures_offset,
            })
        },

        mark_class (Option<Class>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version1(ref header) => header.mark_class_offset,
                Header::Version12(ref header) => header.mark_class_offset,
                Header::Version13(ref header) => header.mark_class_offset,
            })
        },

        marks (Option<Marks>) |this, tape, position| {
            jump_take_maybe!(tape, position, match this.header {
                Header::Version12(ref header) => header.marks_offset,
                Header::Version13(ref header) => header.marks_offset,
                _ => 0,
            })
        },
    }
}

/// The header of a glyph-definition table.
#[derive(Clone, Debug)]
pub enum Header {
    /// Version 1.0.
    Version1(Header1),
    /// Version 1.2.
    Version12(Header12),
    /// Version 1.3.
    Version13(Header13),
}

table! {
    #[doc = "The header of a glyph-definition table of version 1.0."]
    #[derive(Copy)]
    pub Header1 {
        major_version      (u16) = { 1 }, // MajorVersion
        minor_version      (u16) = { 0 }, // MinorVersion
        glyph_class_offset (u16), // GlyphClassDef
        attachments_offset (u16), // AttachList
        ligatures_offset   (u16), // LigCaretList
        mark_class_offset  (u16), // MarkAttachClassDef
    }
}

table! {
    #[doc = "The header of a glyph-definition table of version 1.2."]
    #[derive(Copy)]
    pub Header12 {
        major_version      (u16) = { 1 }, // MajorVersion
        minor_version      (u16) = { 2 }, // MinorVersion
        glyph_class_offset (u16), // GlyphClassDef
        attachments_offset (u16), // AttachList
        ligatures_offset   (u16), // LigCaretList
        mark_class_offset  (u16), // MarkAttachClassDef
        marks_offset       (u16), // MarkGlyphSetsDef
    }
}

table! {
    #[doc = "The header of a glyph-definition table of version 1.3."]
    #[derive(Copy)]
    pub Header13 {
        major_version      (u16) = { 1 }, // MajorVersion
        minor_version      (u16) = { 3 }, // MinorVersion
        glyph_class_offset (u16), // GlyphClassDef
        attachments_offset (u16), // AttachList
        ligatures_offset   (u16), // LigCaretList
        mark_class_offset  (u16), // MarkAttachClassDef
        marks_offset       (u16), // MarkGlyphSetsDef
        variations_offset  (u32), // ItemVarStore
    }
}

impl Value for Header {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        Ok(match try!(tape.peek::<u32>()) {
            0x00010000 => Header::Version1(try!(tape.take())),
            0x00010002 => Header::Version12(try!(tape.take())),
            0x00010003 => Header::Version13(try!(tape.take())),
            _ => raise!("found an unknown format of the glyph-definition table"),
        })
    }
}
<|endoftext|>