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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FormDataBinding;
use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use dom::bindings::codegen::UnionTypes::BlobOrUSVString::{self, eBlob, eUSVString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::USVString;
use dom::blob::Blob;
use dom::file::File;
use dom::htmlformelement::HTMLFormElement;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use string_cache::Atom;
use util::str::DOMString;

#[derive(JSTraceable, Clone)]
#[must_root]
#[derive(HeapSizeOf)]
pub enum FormDatum {
    StringData(String),
    BlobData(JS<Blob>)
}

#[dom_struct]
pub struct FormData {
    reflector_: Reflector,
    data: DOMRefCell<HashMap<Atom, Vec<FormDatum>>>,
    global: GlobalField,
    form: Option<JS<HTMLFormElement>>
}

impl FormData {
    fn new_inherited(form: Option<&HTMLFormElement>, global: GlobalRef) -> FormData {
        FormData {
            reflector_: Reflector::new(),
            data: DOMRefCell::new(HashMap::new()),
            global: GlobalField::from_rooted(&global),
            form: form.map(|f| JS::from_ref(f)),
        }
    }

    pub fn new(form: Option<&HTMLFormElement>, global: GlobalRef) -> Root<FormData> {
        reflect_dom_object(box FormData::new_inherited(form, global),
                           global, FormDataBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, form: Option<&HTMLFormElement>) -> Fallible<Root<FormData>> {
        // TODO: Construct form data set for form if it is supplied
        Ok(FormData::new(form, global))
    }
}

impl FormDataMethods for FormData {
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append(&self, name: USVString, value: USVString) {
        let mut data = self.data.borrow_mut();
        match data.entry(Atom::from(&*name.0)) {
            Occupied(entry) => entry.into_mut().push(FormDatum::StringData(value.0)),
            Vacant  (entry) => { entry.insert(vec!(FormDatum::StringData(value.0))); }
        }
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append_(&self, name: USVString, value: &Blob, filename: Option<USVString>) {
        let blob = FormDatum::BlobData(JS::from_rooted(&self.get_file_or_blob(value, filename)));
        let mut data = self.data.borrow_mut();
        match data.entry(Atom::from(&*name.0)) {
            Occupied(entry) => entry.into_mut().push(blob),
            Vacant(entry) => {
                entry.insert(vec!(blob));
            }
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-delete
    fn Delete(&self, name: USVString) {
        self.data.borrow_mut().remove(&Atom::from(&*name.0));
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-get
    fn Get(&self, name: USVString) -> Option<BlobOrUSVString> {
        self.data.borrow()
                 .get(&Atom::from(&*name.0))
                 .map(|entry| match entry[0] {
                     FormDatum::StringData(ref s) => eUSVString(USVString(s.clone())),
                     FormDatum::BlobData(ref b) => eBlob(Root::from_ref(&*b)),
                 })
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-getall
    fn GetAll(&self, name: USVString) -> Vec<BlobOrUSVString> {
        self.data.borrow()
                 .get(&Atom::from(&*name.0))
                 .map_or(vec![], |data|
                    data.iter().map(|item| match *item {
                        FormDatum::StringData(ref s) => eUSVString(USVString(s.clone())),
                        FormDatum::BlobData(ref b) => eBlob(Root::from_ref(&*b)),
                    }).collect()
                 )
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-has
    fn Has(&self, name: USVString) -> bool {
        self.data.borrow().contains_key(&Atom::from(&*name.0))
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set(&self, name: USVString, value: BlobOrUSVString) {
        let val = match value {
            eUSVString(s) => FormDatum::StringData(s.0),
            eBlob(b) => FormDatum::BlobData(JS::from_rooted(&b))
        };
        self.data.borrow_mut().insert(Atom::from(&*name.0), vec!(val));
    }
}

impl FormData {
    fn get_file_or_blob(&self, value: &Blob, filename: Option<USVString>) -> Root<Blob> {
        match filename {
            Some(fname) => {
                let global = self.global.root();
                let name = DOMString::from(fname.0);
                Root::upcast(File::new(global.r(), value, name))
            }
            None => Root::from_ref(value)
        }
    }
}
<commit_msg>Remove global field from FormData<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FormDataBinding;
use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use dom::bindings::codegen::UnionTypes::BlobOrUSVString::{self, eBlob, eUSVString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::{GlobalRef, global_root_from_reflector};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::USVString;
use dom::blob::Blob;
use dom::file::File;
use dom::htmlformelement::HTMLFormElement;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use string_cache::Atom;
use util::str::DOMString;

#[derive(JSTraceable, Clone)]
#[must_root]
#[derive(HeapSizeOf)]
pub enum FormDatum {
    StringData(String),
    BlobData(JS<Blob>)
}

#[dom_struct]
pub struct FormData {
    reflector_: Reflector,
    data: DOMRefCell<HashMap<Atom, Vec<FormDatum>>>,
    form: Option<JS<HTMLFormElement>>
}

impl FormData {
    fn new_inherited(form: Option<&HTMLFormElement>) -> FormData {
        FormData {
            reflector_: Reflector::new(),
            data: DOMRefCell::new(HashMap::new()),
            form: form.map(|f| JS::from_ref(f)),
        }
    }

    pub fn new(form: Option<&HTMLFormElement>, global: GlobalRef) -> Root<FormData> {
        reflect_dom_object(box FormData::new_inherited(form),
                           global, FormDataBinding::Wrap)
    }

    pub fn Constructor(global: GlobalRef, form: Option<&HTMLFormElement>) -> Fallible<Root<FormData>> {
        // TODO: Construct form data set for form if it is supplied
        Ok(FormData::new(form, global))
    }
}

impl FormDataMethods for FormData {
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append(&self, name: USVString, value: USVString) {
        let mut data = self.data.borrow_mut();
        match data.entry(Atom::from(&*name.0)) {
            Occupied(entry) => entry.into_mut().push(FormDatum::StringData(value.0)),
            Vacant  (entry) => { entry.insert(vec!(FormDatum::StringData(value.0))); }
        }
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-append
    fn Append_(&self, name: USVString, value: &Blob, filename: Option<USVString>) {
        let blob = FormDatum::BlobData(JS::from_rooted(&self.get_file_or_blob(value, filename)));
        let mut data = self.data.borrow_mut();
        match data.entry(Atom::from(&*name.0)) {
            Occupied(entry) => entry.into_mut().push(blob),
            Vacant(entry) => {
                entry.insert(vec!(blob));
            }
        }
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-delete
    fn Delete(&self, name: USVString) {
        self.data.borrow_mut().remove(&Atom::from(&*name.0));
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-get
    fn Get(&self, name: USVString) -> Option<BlobOrUSVString> {
        self.data.borrow()
                 .get(&Atom::from(&*name.0))
                 .map(|entry| match entry[0] {
                     FormDatum::StringData(ref s) => eUSVString(USVString(s.clone())),
                     FormDatum::BlobData(ref b) => eBlob(Root::from_ref(&*b)),
                 })
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-getall
    fn GetAll(&self, name: USVString) -> Vec<BlobOrUSVString> {
        self.data.borrow()
                 .get(&Atom::from(&*name.0))
                 .map_or(vec![], |data|
                    data.iter().map(|item| match *item {
                        FormDatum::StringData(ref s) => eUSVString(USVString(s.clone())),
                        FormDatum::BlobData(ref b) => eBlob(Root::from_ref(&*b)),
                    }).collect()
                 )
    }

    // https://xhr.spec.whatwg.org/#dom-formdata-has
    fn Has(&self, name: USVString) -> bool {
        self.data.borrow().contains_key(&Atom::from(&*name.0))
    }

    #[allow(unrooted_must_root)]
    // https://xhr.spec.whatwg.org/#dom-formdata-set
    fn Set(&self, name: USVString, value: BlobOrUSVString) {
        let val = match value {
            eUSVString(s) => FormDatum::StringData(s.0),
            eBlob(b) => FormDatum::BlobData(JS::from_rooted(&b))
        };
        self.data.borrow_mut().insert(Atom::from(&*name.0), vec!(val));
    }
}

impl FormData {
    fn get_file_or_blob(&self, value: &Blob, filename: Option<USVString>) -> Root<Blob> {
        match filename {
            Some(fname) => {
                let global = global_root_from_reflector(self);
                let name = DOMString::from(fname.0);
                Root::upcast(File::new(global.r(), value, name))
            }
            None => Root::from_ref(value)
        }
    }
}
<|endoftext|>


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

use attr::{AttrIdentifier, AttrValue};
use element_state::*;
use selector_impl::SelectorImplExt;
use selectors::Element;
use selectors::matching::matches_compound_selector;
use selectors::parser::{AttrSelector, Combinator, CompoundSelector, NamespaceConstraint, SelectorImpl, SimpleSelector};
use std::clone::Clone;
use std::sync::Arc;
use string_cache::{Atom, Namespace};

/// When the ElementState of an element (like IN_HOVER_STATE) changes, certain
/// pseudo-classes (like :hover) may require us to restyle that element, its
/// siblings, and/or its descendants. Similarly, when various attributes of an
/// element change, we may also need to restyle things with id, class, and attribute
/// selectors. Doing this conservatively is expensive, and so we use RestyleHints to
/// short-circuit work we know is unnecessary.

bitflags! {
    flags RestyleHint: u8 {
        #[doc = "Rerun selector matching on the element."]
        const RESTYLE_SELF = 0x01,
        #[doc = "Rerun selector matching on all of the element's descendants."]
        // NB: In Gecko, we have RESTYLE_SUBTREE which is inclusive of self, but heycam isn't aware
        // of a good reason for that.
        const RESTYLE_DESCENDANTS = 0x02,
        #[doc = "Rerun selector matching on all later siblings of the element and all of their descendants."]
        const RESTYLE_LATER_SIBLINGS = 0x04,
    }
}

/// In order to compute restyle hints, we perform a selector match against a list of partial
/// selectors whose rightmost simple selector may be sensitive to the thing being changed. We
/// do this matching twice, once for the element as it exists now and once for the element as it
/// existed at the time of the last restyle. If the results of the selector match differ, that means
/// that the given partial selector is sensitive to the change, and we compute a restyle hint
/// based on its combinator.
///
/// In order to run selector matching against the old element state, we generate a wrapper for
/// the element which claims to have the old state. This is the ElementWrapper logic below.
///
/// Gecko does this differently for element states, and passes a mask called mStateMask, which
/// indicates the states that need to be ignored during selector matching. This saves an ElementWrapper
/// allocation and an additional selector match call at the expense of additional complexity inside
/// the selector matching logic. This only works for boolean states though, so we still need to
/// take the ElementWrapper approach for attribute-dependent style. So we do it the same both ways for
/// now to reduce complexity, but it's worth measuring the performance impact (if any) of the
/// mStateMask approach.

#[derive(HeapSizeOf, Clone)]
pub struct ElementSnapshot {
    pub state: Option<ElementState>,
    pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
}

impl ElementSnapshot {
    pub fn new() -> ElementSnapshot {
        EMPTY_SNAPSHOT.clone()
    }

    // Gets an attribute matching |namespace| and |name|, if any. Panics if |attrs| is None.
    pub fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&AttrValue> {
        self.attrs.as_ref().unwrap().iter()
                  .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
                  .map(|&(_, ref v)| v)
    }

    // Gets an attribute matching |name| if any, ignoring namespace. Panics if |attrs| is None.
    pub fn get_attr_ignore_ns(&self, name: &Atom) -> Option<&AttrValue> {
        self.attrs.as_ref().unwrap().iter()
                  .find(|&&(ref ident, _)| ident.local_name == *name)
                  .map(|&(_, ref v)| v)
    }
}

static EMPTY_SNAPSHOT: ElementSnapshot = ElementSnapshot { state: None, attrs: None };

struct ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    element: E,
    snapshot: &'a ElementSnapshot,
}

impl<'a, E> ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    pub fn new(el: E) -> ElementWrapper<'a, E> {
        ElementWrapper { element: el, snapshot: &EMPTY_SNAPSHOT }
    }

    pub fn new_with_snapshot(el: E, snapshot: &'a ElementSnapshot) -> ElementWrapper<'a, E> {
        ElementWrapper { element: el, snapshot: snapshot }
    }
}

impl<'a, E> Element for ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    type Impl = E::Impl;

    fn match_non_ts_pseudo_class(&self,
                                 pseudo_class: <Self::Impl as SelectorImpl>::NonTSPseudoClass) -> bool {
        let flag = Self::Impl::pseudo_class_state_flag(&pseudo_class);
        if flag == ElementState::empty() {
            self.element.match_non_ts_pseudo_class(pseudo_class)
        } else {
            match self.snapshot.state {
                Some(s) => s.contains(flag),
                None => self.element.match_non_ts_pseudo_class(pseudo_class)
            }
        }
    }

    fn parent_element(&self) -> Option<Self> {
        self.element.parent_element().map(ElementWrapper::new)
    }
    fn first_child_element(&self) -> Option<Self> {
        self.element.first_child_element().map(ElementWrapper::new)
    }
    fn last_child_element(&self) -> Option<Self> {
        self.element.last_child_element().map(ElementWrapper::new)
    }
    fn prev_sibling_element(&self) -> Option<Self> {
        self.element.prev_sibling_element().map(ElementWrapper::new)
    }
    fn next_sibling_element(&self) -> Option<Self> {
        self.element.next_sibling_element().map(ElementWrapper::new)
    }
    fn is_html_element_in_html_document(&self) -> bool {
        self.element.is_html_element_in_html_document()
    }
    fn get_local_name(&self) -> &Atom {
        self.element.get_local_name()
    }
    fn get_namespace<'b>(&self) -> &Namespace {
        self.element.get_namespace()
    }
    fn get_id(&self) -> Option<Atom> {
        match self.snapshot.attrs {
            Some(_) => self.snapshot.get_attr(&ns!(), &atom!("id")).map(|value| value.as_atom().clone()),
            None => self.element.get_id(),
        }
    }
    fn has_class(&self, name: &Atom) -> bool {
        match self.snapshot.attrs {
            Some(_) => self.snapshot.get_attr(&ns!(), &atom!("class"))
                                    .map_or(false, |v| { v.as_tokens().iter().any(|atom| atom == name) }),
            None => self.element.has_class(name),
        }
    }
    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
                    where F: Fn(&str) -> bool {
        match self.snapshot.attrs {
            Some(_) => {
                let html = self.is_html_element_in_html_document();
                let local_name = if html { &attr.lower_name } else { &attr.name };
                match attr.namespace {
                    NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name),
                    NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name),
                }.map_or(false, |v| test(v))
            },
            None => self.element.match_attr(attr, test)
        }
    }
    fn is_empty(&self) -> bool {
        self.element.is_empty()
    }
    fn is_root(&self) -> bool {
        self.element.is_root()
    }
    fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
        match self.snapshot.attrs {
            Some(_) => {
                if let Some(v) = self.snapshot.get_attr(&ns!(), &atom!("class")) {
                    for c in v.as_tokens() { callback(c) }
                }
            }
            None => self.element.each_class(callback),
        }
    }
}

fn selector_to_state<Impl: SelectorImplExt>(sel: &SimpleSelector<Impl>) -> ElementState {
    match *sel {
        SimpleSelector::NonTSPseudoClass(ref pc) => Impl::pseudo_class_state_flag(pc),
        _ => ElementState::empty(),
    }
}

fn is_attr_selector<Impl: SelectorImpl>(sel: &SimpleSelector<Impl>) -> bool {
    match *sel {
        SimpleSelector::ID(_) |
        SimpleSelector::Class(_) |
        SimpleSelector::AttrExists(_) |
        SimpleSelector::AttrEqual(_, _, _) |
        SimpleSelector::AttrIncludes(_, _) |
        SimpleSelector::AttrDashMatch(_, _, _) |
        SimpleSelector::AttrPrefixMatch(_, _) |
        SimpleSelector::AttrSubstringMatch(_, _) |
        SimpleSelector::AttrSuffixMatch(_, _) => true,
        _ => false,
    }
}

fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
    match combinator {
        None => RESTYLE_SELF,
        Some(c) => match c {
            Combinator::Child => RESTYLE_DESCENDANTS,
            Combinator::Descendant => RESTYLE_DESCENDANTS,
            Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
            Combinator::LaterSibling => RESTYLE_LATER_SIBLINGS,
        }
    }
}

#[derive(Debug, HeapSizeOf)]
struct Sensitivities {
    pub states: ElementState,
    pub attrs: bool,
}

impl Sensitivities {
    fn is_empty(&self) -> bool {
        self.states.is_empty() && !self.attrs
    }

    fn new() -> Sensitivities {
        Sensitivities {
            states: ElementState::empty(),
            attrs: false,
        }
    }
}

// Mapping between (partial) CompoundSelectors (and the combinator to their right)
// and the states and attributes they depend on.
//
// In general, for all selectors in all applicable stylesheets of the form:
//
// |a _ b _ c _ d _ e|
//
// Where:
//   * |b| and |d| are simple selectors that depend on state (like :hover) or
//     attributes (like [attr...], .foo, or #foo).
//   * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on
//     state or attributes.
//
// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|, even
// though those selectors may not appear on their own in any stylesheet. This allows
// us to quickly scan through the dependency sites of all style rules and determine the
// maximum effect that a given state or attribute change may have on the style of
// elements in the document.
#[derive(Debug, HeapSizeOf)]
struct Dependency<Impl: SelectorImplExt> {
    selector: Arc<CompoundSelector<Impl>>,
    combinator: Option<Combinator>,
    sensitivities: Sensitivities,
}

#[derive(Debug, HeapSizeOf)]
pub struct DependencySet<Impl: SelectorImplExt> {
    deps: Vec<Dependency<Impl>>,
}

impl<Impl: SelectorImplExt> DependencySet<Impl> {
    pub fn new() -> DependencySet<Impl> {
        DependencySet { deps: Vec::new() }
    }

    pub fn compute_hint<E>(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState)
                          -> RestyleHint
                          where E: Element<Impl=Impl> + Clone {
        let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state);
        let attrs_changed = snapshot.attrs.is_some();
        let mut hint = RestyleHint::empty();
        for dep in &self.deps {
            if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) {
                let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
                let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false);
                let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false);
                if matched_then != matches_now {
                    hint.insert(combinator_to_restyle_hint(dep.combinator));
                    if hint.is_all() {
                        break
                    }
                }
            }
        }
        hint
    }

    pub fn note_selector(&mut self, selector: Arc<CompoundSelector<Impl>>) {
        let mut cur = selector;
        let mut combinator: Option<Combinator> = None;
        loop {
            let mut sensitivities = Sensitivities::new();
            for s in &cur.simple_selectors {
                sensitivities.states.insert(selector_to_state(s));
                if !sensitivities.attrs {
                    sensitivities.attrs = is_attr_selector(s);
                }
            }
            if !sensitivities.is_empty() {
                self.deps.push(Dependency {
                    selector: cur.clone(),
                    combinator: combinator,
                    sensitivities: sensitivities,
                });
            }

            cur = match cur.next {
                Some((ref sel, comb)) => {
                    combinator = Some(comb);
                    sel.clone()
                }
                None => break,
            }
        }
    }

    pub fn clear(&mut self) {
        self.deps.clear();
    }
}
<commit_msg>style: Remove unneeded lifetime<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use attr::{AttrIdentifier, AttrValue};
use element_state::*;
use selector_impl::SelectorImplExt;
use selectors::Element;
use selectors::matching::matches_compound_selector;
use selectors::parser::{AttrSelector, Combinator, CompoundSelector, NamespaceConstraint, SelectorImpl, SimpleSelector};
use std::clone::Clone;
use std::sync::Arc;
use string_cache::{Atom, Namespace};

/// When the ElementState of an element (like IN_HOVER_STATE) changes, certain
/// pseudo-classes (like :hover) may require us to restyle that element, its
/// siblings, and/or its descendants. Similarly, when various attributes of an
/// element change, we may also need to restyle things with id, class, and attribute
/// selectors. Doing this conservatively is expensive, and so we use RestyleHints to
/// short-circuit work we know is unnecessary.

bitflags! {
    flags RestyleHint: u8 {
        #[doc = "Rerun selector matching on the element."]
        const RESTYLE_SELF = 0x01,
        #[doc = "Rerun selector matching on all of the element's descendants."]
        // NB: In Gecko, we have RESTYLE_SUBTREE which is inclusive of self, but heycam isn't aware
        // of a good reason for that.
        const RESTYLE_DESCENDANTS = 0x02,
        #[doc = "Rerun selector matching on all later siblings of the element and all of their descendants."]
        const RESTYLE_LATER_SIBLINGS = 0x04,
    }
}

/// In order to compute restyle hints, we perform a selector match against a list of partial
/// selectors whose rightmost simple selector may be sensitive to the thing being changed. We
/// do this matching twice, once for the element as it exists now and once for the element as it
/// existed at the time of the last restyle. If the results of the selector match differ, that means
/// that the given partial selector is sensitive to the change, and we compute a restyle hint
/// based on its combinator.
///
/// In order to run selector matching against the old element state, we generate a wrapper for
/// the element which claims to have the old state. This is the ElementWrapper logic below.
///
/// Gecko does this differently for element states, and passes a mask called mStateMask, which
/// indicates the states that need to be ignored during selector matching. This saves an ElementWrapper
/// allocation and an additional selector match call at the expense of additional complexity inside
/// the selector matching logic. This only works for boolean states though, so we still need to
/// take the ElementWrapper approach for attribute-dependent style. So we do it the same both ways for
/// now to reduce complexity, but it's worth measuring the performance impact (if any) of the
/// mStateMask approach.

#[derive(HeapSizeOf, Clone)]
pub struct ElementSnapshot {
    pub state: Option<ElementState>,
    pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
}

impl ElementSnapshot {
    pub fn new() -> ElementSnapshot {
        EMPTY_SNAPSHOT.clone()
    }

    // Gets an attribute matching |namespace| and |name|, if any. Panics if |attrs| is None.
    pub fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&AttrValue> {
        self.attrs.as_ref().unwrap().iter()
                  .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
                  .map(|&(_, ref v)| v)
    }

    // Gets an attribute matching |name| if any, ignoring namespace. Panics if |attrs| is None.
    pub fn get_attr_ignore_ns(&self, name: &Atom) -> Option<&AttrValue> {
        self.attrs.as_ref().unwrap().iter()
                  .find(|&&(ref ident, _)| ident.local_name == *name)
                  .map(|&(_, ref v)| v)
    }
}

static EMPTY_SNAPSHOT: ElementSnapshot = ElementSnapshot { state: None, attrs: None };

struct ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    element: E,
    snapshot: &'a ElementSnapshot,
}

impl<'a, E> ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    pub fn new(el: E) -> ElementWrapper<'a, E> {
        ElementWrapper { element: el, snapshot: &EMPTY_SNAPSHOT }
    }

    pub fn new_with_snapshot(el: E, snapshot: &'a ElementSnapshot) -> ElementWrapper<'a, E> {
        ElementWrapper { element: el, snapshot: snapshot }
    }
}

impl<'a, E> Element for ElementWrapper<'a, E>
    where E: Element,
          E::Impl: SelectorImplExt {
    type Impl = E::Impl;

    fn match_non_ts_pseudo_class(&self,
                                 pseudo_class: <Self::Impl as SelectorImpl>::NonTSPseudoClass) -> bool {
        let flag = Self::Impl::pseudo_class_state_flag(&pseudo_class);
        if flag == ElementState::empty() {
            self.element.match_non_ts_pseudo_class(pseudo_class)
        } else {
            match self.snapshot.state {
                Some(s) => s.contains(flag),
                None => self.element.match_non_ts_pseudo_class(pseudo_class)
            }
        }
    }

    fn parent_element(&self) -> Option<Self> {
        self.element.parent_element().map(ElementWrapper::new)
    }
    fn first_child_element(&self) -> Option<Self> {
        self.element.first_child_element().map(ElementWrapper::new)
    }
    fn last_child_element(&self) -> Option<Self> {
        self.element.last_child_element().map(ElementWrapper::new)
    }
    fn prev_sibling_element(&self) -> Option<Self> {
        self.element.prev_sibling_element().map(ElementWrapper::new)
    }
    fn next_sibling_element(&self) -> Option<Self> {
        self.element.next_sibling_element().map(ElementWrapper::new)
    }
    fn is_html_element_in_html_document(&self) -> bool {
        self.element.is_html_element_in_html_document()
    }
    fn get_local_name(&self) -> &Atom {
        self.element.get_local_name()
    }
    fn get_namespace(&self) -> &Namespace {
        self.element.get_namespace()
    }
    fn get_id(&self) -> Option<Atom> {
        match self.snapshot.attrs {
            Some(_) => self.snapshot.get_attr(&ns!(), &atom!("id")).map(|value| value.as_atom().clone()),
            None => self.element.get_id(),
        }
    }
    fn has_class(&self, name: &Atom) -> bool {
        match self.snapshot.attrs {
            Some(_) => self.snapshot.get_attr(&ns!(), &atom!("class"))
                                    .map_or(false, |v| { v.as_tokens().iter().any(|atom| atom == name) }),
            None => self.element.has_class(name),
        }
    }
    fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
                    where F: Fn(&str) -> bool {
        match self.snapshot.attrs {
            Some(_) => {
                let html = self.is_html_element_in_html_document();
                let local_name = if html { &attr.lower_name } else { &attr.name };
                match attr.namespace {
                    NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name),
                    NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name),
                }.map_or(false, |v| test(v))
            },
            None => self.element.match_attr(attr, test)
        }
    }
    fn is_empty(&self) -> bool {
        self.element.is_empty()
    }
    fn is_root(&self) -> bool {
        self.element.is_root()
    }
    fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
        match self.snapshot.attrs {
            Some(_) => {
                if let Some(v) = self.snapshot.get_attr(&ns!(), &atom!("class")) {
                    for c in v.as_tokens() { callback(c) }
                }
            }
            None => self.element.each_class(callback),
        }
    }
}

fn selector_to_state<Impl: SelectorImplExt>(sel: &SimpleSelector<Impl>) -> ElementState {
    match *sel {
        SimpleSelector::NonTSPseudoClass(ref pc) => Impl::pseudo_class_state_flag(pc),
        _ => ElementState::empty(),
    }
}

fn is_attr_selector<Impl: SelectorImpl>(sel: &SimpleSelector<Impl>) -> bool {
    match *sel {
        SimpleSelector::ID(_) |
        SimpleSelector::Class(_) |
        SimpleSelector::AttrExists(_) |
        SimpleSelector::AttrEqual(_, _, _) |
        SimpleSelector::AttrIncludes(_, _) |
        SimpleSelector::AttrDashMatch(_, _, _) |
        SimpleSelector::AttrPrefixMatch(_, _) |
        SimpleSelector::AttrSubstringMatch(_, _) |
        SimpleSelector::AttrSuffixMatch(_, _) => true,
        _ => false,
    }
}

fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
    match combinator {
        None => RESTYLE_SELF,
        Some(c) => match c {
            Combinator::Child => RESTYLE_DESCENDANTS,
            Combinator::Descendant => RESTYLE_DESCENDANTS,
            Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
            Combinator::LaterSibling => RESTYLE_LATER_SIBLINGS,
        }
    }
}

#[derive(Debug, HeapSizeOf)]
struct Sensitivities {
    pub states: ElementState,
    pub attrs: bool,
}

impl Sensitivities {
    fn is_empty(&self) -> bool {
        self.states.is_empty() && !self.attrs
    }

    fn new() -> Sensitivities {
        Sensitivities {
            states: ElementState::empty(),
            attrs: false,
        }
    }
}

// Mapping between (partial) CompoundSelectors (and the combinator to their right)
// and the states and attributes they depend on.
//
// In general, for all selectors in all applicable stylesheets of the form:
//
// |a _ b _ c _ d _ e|
//
// Where:
//   * |b| and |d| are simple selectors that depend on state (like :hover) or
//     attributes (like [attr...], .foo, or #foo).
//   * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on
//     state or attributes.
//
// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|, even
// though those selectors may not appear on their own in any stylesheet. This allows
// us to quickly scan through the dependency sites of all style rules and determine the
// maximum effect that a given state or attribute change may have on the style of
// elements in the document.
#[derive(Debug, HeapSizeOf)]
struct Dependency<Impl: SelectorImplExt> {
    selector: Arc<CompoundSelector<Impl>>,
    combinator: Option<Combinator>,
    sensitivities: Sensitivities,
}

#[derive(Debug, HeapSizeOf)]
pub struct DependencySet<Impl: SelectorImplExt> {
    deps: Vec<Dependency<Impl>>,
}

impl<Impl: SelectorImplExt> DependencySet<Impl> {
    pub fn new() -> DependencySet<Impl> {
        DependencySet { deps: Vec::new() }
    }

    pub fn compute_hint<E>(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState)
                          -> RestyleHint
                          where E: Element<Impl=Impl> + Clone {
        let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state);
        let attrs_changed = snapshot.attrs.is_some();
        let mut hint = RestyleHint::empty();
        for dep in &self.deps {
            if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) {
                let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
                let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false);
                let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false);
                if matched_then != matches_now {
                    hint.insert(combinator_to_restyle_hint(dep.combinator));
                    if hint.is_all() {
                        break
                    }
                }
            }
        }
        hint
    }

    pub fn note_selector(&mut self, selector: Arc<CompoundSelector<Impl>>) {
        let mut cur = selector;
        let mut combinator: Option<Combinator> = None;
        loop {
            let mut sensitivities = Sensitivities::new();
            for s in &cur.simple_selectors {
                sensitivities.states.insert(selector_to_state(s));
                if !sensitivities.attrs {
                    sensitivities.attrs = is_attr_selector(s);
                }
            }
            if !sensitivities.is_empty() {
                self.deps.push(Dependency {
                    selector: cur.clone(),
                    combinator: combinator,
                    sensitivities: sensitivities,
                });
            }

            cur = match cur.next {
                Some((ref sel, comb)) => {
                    combinator = Some(comb);
                    sel.clone()
                }
                None => break,
            }
        }
    }

    pub fn clear(&mut self) {
        self.deps.clear();
    }
}
<|endoftext|>


<commit_before>//! A collection of default help commands for the framework.
//!
//! # Example
//!
//! Using the [`with_embeds`] function to have the framework's help message use
//! embeds:
//!
//! ```rs,no_run
//! use serenity::framework::standard::help_commands;
//! use serenity::Client;
//! use std::env;
//!
//! let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap());
//! use serenity::framework::StandardFramework;
//!
//! client.with_framework(StandardFramework::new()
//!     .command("help", |c| c.exec_help(help_commands::with_embeds)));
//! ```
//!
//! The same can be accomplished with no embeds by substituting `with_embeds`
//! with the [`plain`] function.
//!
//! [`plain`]: fn.plain.html
//! [`with_embeds`]: fn.with_embeds.html

use std::collections::HashMap;
use std::sync::Arc;
use std::fmt::Write;
use super::command::InternalCommand;
use super::{Args, Command, CommandGroup, CommandOrAlias, CommandError};
use client::Context;
use model::{ChannelId, Message};
use utils::Colour;
use framework::standard::{has_correct_roles, has_correct_permissions};

fn error_embed(channel_id: &ChannelId, input: &str) {
    let _ = channel_id.send_message(|m| {
        m.embed(|e| e.colour(Colour::dark_red()).description(input))
    });
}

fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &InternalCommand> {
    let mut result = HashMap::new();

    for (n, v) in cmds {
        if let CommandOrAlias::Command(ref cmd) = *v {
            result.insert(n, cmd);
        }
    }

    result
}

/// Checks whether a user is member of required roles
/// and given the required permissions.
pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool {
    if let Some(guild) = msg.guild() {
        let guild = guild.read().unwrap();

        if let Some(member) = guild.members.get(&msg.author.id) {

            if let Ok(permissions) = member.permissions() {

                if cmd.allowed_roles.is_empty() {
                    return permissions.administrator() || has_correct_permissions(&cmd, &msg);
                } else {
                    return permissions.administrator() || (has_correct_roles(&cmd, &guild, &member) && has_correct_permissions(cmd, msg));
                }
            }
        }
    }
    false
}

/// Posts an embed showing each individual command group and its commands.
///
/// # Examples
///
/// Use the command with `exec_help`:
///
/// ```rust
/// # use serenity::prelude::*;
/// # struct Handler;
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler);
/// #
/// use serenity::framework::standard::{StandardFramework, help_commands};
///
/// client.with_framework(StandardFramework::new()
///     .command("help", |c| c.exec_help(help_commands::with_embeds)));
/// ```
pub fn with_embeds(_: &mut Context,
                   msg: &Message,
                   groups: HashMap<String, Arc<CommandGroup>>,
                   args: Args)
                   -> Result<(), CommandError> {
    if !args.is_empty() {
        let name = args.full();

        for (group_name, group) in groups {
            let mut found: Option<(&String, &InternalCommand)> = None;

            for (command_name, command) in &group.commands {
                let with_prefix = if let Some(ref prefix) = group.prefix {
                    format!("{} {}", prefix, command_name)
                } else {
                    command_name.to_string()
                };

                if name == with_prefix || name == *command_name {
                    match *command {
                        CommandOrAlias::Command(ref cmd) => {
                            if has_all_requirements(&cmd, &msg) {
                                found = Some((command_name, cmd));
                            }
                            else {
                                break;
                            }
                        },
                        CommandOrAlias::Alias(ref name) => {
                            let actual_command = group.commands.get(name).unwrap();

                            match *actual_command {
                                CommandOrAlias::Command(ref cmd) => {
                                    if has_all_requirements(&cmd, &msg) {
                                        found = Some((name, cmd));
                                    }
                                    else {
                                        break;
                                    }
                                },

                                CommandOrAlias::Alias(ref name) => {
                                    let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name));
                                    return Ok(());
                                },
                            }
                        },
                    }
                }
            }

            if let Some((command_name, command)) = found {
                if !command.help_available {
                    error_embed(&msg.channel_id, "**Error**: No help available.");

                    return Ok(());
                }

                let _ = msg.channel_id.send_message(|m| {
                    m.embed(|e| {
                        let mut embed = e.colour(Colour::rosewater()).title(command_name);

                        if let Some(ref desc) = command.desc {
                            embed = embed.description(desc);
                        }

                        if let Some(ref usage) = command.usage {
                            embed = embed.field(|f| {
                                f.name("Usage")
                                    .value(&format!("`{} {}`", command_name, usage))
                            });
                        }

                        if let Some(ref example) = command.example {
                            embed = embed.field(|f| {
                                f.name("Sample usage")
                                    .value(&format!("`{} {}`", command_name, example))
                            });
                        }

                        if group_name != "Ungrouped" {
                            embed = embed.field(|f| f.name("Group").value(&group_name));
                        }

                        if !command.aliases.is_empty() {
                            let aliases = command.aliases.join(", ");
                            embed = embed.field(|f| f.name("Aliases").value(&aliases));
                        }

                        let available = if command.dm_only {
                            "Only in DM"
                        } else if command.guild_only {
                            "Only in guilds"
                        } else {
                            "In DM and guilds"
                        };

                        embed = embed.field(|f| f.name("Available").value(available));

                        embed
                    })
                });

                return Ok(());
            }
        }

        let error_msg = format!("**Error**: Command `{}` not found.", name);
        error_embed(&msg.channel_id, &error_msg);

        return Ok(());
    }

    let _ = msg.channel_id.send_message(|m| {
        m.embed(|mut e| {
            e = e.colour(Colour::rosewater()).description(
                "To get help with an individual command, pass its \
                 name as an argument to this command.",
            );

            let mut group_names = groups.keys().collect::<Vec<_>>();
            group_names.sort();

            for group_name in group_names {
                let group = &groups[group_name];
                let mut desc = String::new();

                if let Some(ref x) = group.prefix {
                    let _ = write!(desc, "Prefix: {}\n", x);
                }

                let mut has_commands = false;

                let commands = remove_aliases(&group.commands);
                let mut command_names = commands.keys().collect::<Vec<_>>();
                command_names.sort();

                for name in command_names {
                    let cmd = &commands[name];

                    if cmd.help_available {

                        if cmd.help_available && has_all_requirements(&cmd, &msg) {
                            let _ = write!(desc, "`{}`\n", name);
                            has_commands = true;
                        }
                    }
                }

                if has_commands {
                    e = e.field(|f| f.name(group_name).value(&desc));
                }
            }
            e
        })
    });

    Ok(())
}

/// Posts formatted text displaying each individual command group and its commands.
///
/// # Examples
///
/// Use the command with `exec_help`:
///
/// ```rust
/// # use serenity::prelude::*;
/// # struct Handler;
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler);
/// #
/// use serenity::framework::standard::{StandardFramework, help_commands};
///
/// client.with_framework(StandardFramework::new()
///     .command("help", |c| c.exec_help(help_commands::plain)));
/// ```
pub fn plain(_: &mut Context,
             msg: &Message,
             groups: HashMap<String, Arc<CommandGroup>>,
             args: Args)
             -> Result<(), CommandError> {
    if !args.is_empty() {
        let name = args.full();

        for (group_name, group) in groups {
            let mut found: Option<(&String, &Command)> = None;

            for (command_name, command) in &group.commands {
                let with_prefix = if let Some(ref prefix) = group.prefix {
                    format!("{} {}", prefix, command_name)
                } else {
                    command_name.to_string()
                };

                if name == with_prefix || name == *command_name {
                    match *command {
                        CommandOrAlias::Command(ref cmd) => {
                            if has_all_requirements(&cmd, &msg) {
                                found = Some((command_name, cmd));
                            }
                            else {
                                break;
                            }
                        },
                        CommandOrAlias::Alias(ref name) => {
                            let actual_command = group.commands.get(name).unwrap();

                            match *actual_command {
                                CommandOrAlias::Command(ref cmd) => {
                                    if has_all_requirements(&cmd, &msg) {
                                        found = Some((name, cmd));
                                    }
                                    else {
                                        break;
                                    }
                                },

                                CommandOrAlias::Alias(ref name) => {
                                    let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name));
                                    return Ok(());
                                },
                            }
                        },
                    }
                }
            }

            if let Some((command_name, command)) = found {
                if !command.help_available {
                    let _ = msg.channel_id.say("**Error**: No help available.");
                    return Ok(());
                }

                let mut result = format!("**{}**\n", command_name);

                if !command.aliases.is_empty() {
                    let aliases = command.aliases.join("`, `");
                    let _ = write!(result, "**Aliases:** `{}`\n", aliases);
                }

                if let Some(ref desc) = command.desc {
                    let _ = write!(result, "**Description:** {}\n", desc);
                }

                if let Some(ref usage) = command.usage {
                    let _ = write!(result, "**Usage:** `{} {}`\n", command_name, usage);
                }

                if let Some(ref example) = command.example {
                    let _ = write!(result, "**Sample usage:** `{} {}`\n", command_name, example);
                }

                if group_name != "Ungrouped" {
                    let _ = write!(result, "**Group:** {}\n", group_name);
                }

                let only = if command.dm_only {
                    "Only in DM"
                } else if command.guild_only {
                    "Only in guilds"
                } else {
                    "In DM and guilds"
                };

                result.push_str("**Available:** ");
                result.push_str(only);
                result.push_str("\n");

                let _ = msg.channel_id.say(&result);

                return Ok(());
            }
        }

        let _ = msg.channel_id
            .say(&format!("**Error**: Command `{}` not found.", name));

        return Ok(());
    }

    let mut result = "**Commands**\nTo get help with an individual command, pass its \
                      name as an argument to this command.\n\n"
        .to_string();

    let mut group_names = groups.keys().collect::<Vec<_>>();
    group_names.sort();

    for group_name in group_names {
        let group = &groups[group_name];
        let mut group_help = String::new();

        let commands = remove_aliases(&group.commands);
        let mut command_names = commands.keys().collect::<Vec<_>>();
        command_names.sort();

        for name in command_names {
            let cmd = &commands[name];

            if cmd.help_available && has_all_requirements(&cmd, &msg) {
                let _ = write!(group_help, "`{}` ", name);
            }
        }

        if !group_help.is_empty() {
            let _ = write!(result, "**{}:** ", group_name);

            if let Some(ref x) = group.prefix {
                let _ = write!(result, "(prefix: `{}`): ", x);
            }

            result.push_str(&group_help);
            result.push('\n');
        }
    }

    let _ = msg.channel_id.say(&result);

    Ok(())
}
<commit_msg>Fix clippy warnings<commit_after>//! A collection of default help commands for the framework.
//!
//! # Example
//!
//! Using the [`with_embeds`] function to have the framework's help message use
//! embeds:
//!
//! ```rs,no_run
//! use serenity::framework::standard::help_commands;
//! use serenity::Client;
//! use std::env;
//!
//! let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap());
//! use serenity::framework::StandardFramework;
//!
//! client.with_framework(StandardFramework::new()
//!     .command("help", |c| c.exec_help(help_commands::with_embeds)));
//! ```
//!
//! The same can be accomplished with no embeds by substituting `with_embeds`
//! with the [`plain`] function.
//!
//! [`plain`]: fn.plain.html
//! [`with_embeds`]: fn.with_embeds.html

use std::collections::HashMap;
use std::sync::Arc;
use std::fmt::Write;
use super::command::InternalCommand;
use super::{Args, Command, CommandGroup, CommandOrAlias, CommandError};
use client::Context;
use model::{ChannelId, Message};
use utils::Colour;
use framework::standard::{has_correct_roles, has_correct_permissions};

fn error_embed(channel_id: &ChannelId, input: &str) {
    let _ = channel_id.send_message(|m| {
        m.embed(|e| e.colour(Colour::dark_red()).description(input))
    });
}

fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &InternalCommand> {
    let mut result = HashMap::new();

    for (n, v) in cmds {
        if let CommandOrAlias::Command(ref cmd) = *v {
            result.insert(n, cmd);
        }
    }

    result
}

/// Checks whether a user is member of required roles
/// and given the required permissions.
pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool {
    if let Some(guild) = msg.guild() {
        let guild = guild.read().unwrap();

        if let Some(member) = guild.members.get(&msg.author.id) {

            if let Ok(permissions) = member.permissions() {

                if cmd.allowed_roles.is_empty() {
                    return permissions.administrator() || has_correct_permissions(cmd, msg);
                } else {
                    return permissions.administrator() || (has_correct_roles(cmd, &guild, member) && has_correct_permissions(cmd, msg));
                }
            }
        }
    }
    false
}

/// Posts an embed showing each individual command group and its commands.
///
/// # Examples
///
/// Use the command with `exec_help`:
///
/// ```rust
/// # use serenity::prelude::*;
/// # struct Handler;
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler);
/// #
/// use serenity::framework::standard::{StandardFramework, help_commands};
///
/// client.with_framework(StandardFramework::new()
///     .command("help", |c| c.exec_help(help_commands::with_embeds)));
/// ```
pub fn with_embeds(_: &mut Context,
                   msg: &Message,
                   groups: HashMap<String, Arc<CommandGroup>>,
                   args: Args)
                   -> Result<(), CommandError> {
    if !args.is_empty() {
        let name = args.full();

        for (group_name, group) in groups {
            let mut found: Option<(&String, &InternalCommand)> = None;

            for (command_name, command) in &group.commands {
                let with_prefix = if let Some(ref prefix) = group.prefix {
                    format!("{} {}", prefix, command_name)
                } else {
                    command_name.to_string()
                };

                if name == with_prefix || name == *command_name {
                    match *command {
                        CommandOrAlias::Command(ref cmd) => {
                            if has_all_requirements(cmd, msg) {
                                found = Some((command_name, cmd));
                            }
                            else {
                                break;
                            }
                        },
                        CommandOrAlias::Alias(ref name) => {
                            let actual_command = &group.commands[name];

                            match *actual_command {
                                CommandOrAlias::Command(ref cmd) => {
                                    if has_all_requirements(cmd, msg) {
                                        found = Some((name, cmd));
                                    }
                                    else {
                                        break;
                                    }
                                },

                                CommandOrAlias::Alias(ref name) => {
                                    let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name));
                                    return Ok(());
                                },
                            }
                        },
                    }
                }
            }

            if let Some((command_name, command)) = found {
                if !command.help_available {
                    error_embed(&msg.channel_id, "**Error**: No help available.");

                    return Ok(());
                }

                let _ = msg.channel_id.send_message(|m| {
                    m.embed(|e| {
                        let mut embed = e.colour(Colour::rosewater()).title(command_name);

                        if let Some(ref desc) = command.desc {
                            embed = embed.description(desc);
                        }

                        if let Some(ref usage) = command.usage {
                            embed = embed.field(|f| {
                                f.name("Usage")
                                    .value(&format!("`{} {}`", command_name, usage))
                            });
                        }

                        if let Some(ref example) = command.example {
                            embed = embed.field(|f| {
                                f.name("Sample usage")
                                    .value(&format!("`{} {}`", command_name, example))
                            });
                        }

                        if group_name != "Ungrouped" {
                            embed = embed.field(|f| f.name("Group").value(&group_name));
                        }

                        if !command.aliases.is_empty() {
                            let aliases = command.aliases.join(", ");
                            embed = embed.field(|f| f.name("Aliases").value(&aliases));
                        }

                        let available = if command.dm_only {
                            "Only in DM"
                        } else if command.guild_only {
                            "Only in guilds"
                        } else {
                            "In DM and guilds"
                        };

                        embed = embed.field(|f| f.name("Available").value(available));

                        embed
                    })
                });

                return Ok(());
            }
        }

        let error_msg = format!("**Error**: Command `{}` not found.", name);
        error_embed(&msg.channel_id, &error_msg);

        return Ok(());
    }

    let _ = msg.channel_id.send_message(|m| {
        m.embed(|mut e| {
            e = e.colour(Colour::rosewater()).description(
                "To get help with an individual command, pass its \
                 name as an argument to this command.",
            );

            let mut group_names = groups.keys().collect::<Vec<_>>();
            group_names.sort();

            for group_name in group_names {
                let group = &groups[group_name];
                let mut desc = String::new();

                if let Some(ref x) = group.prefix {
                    let _ = write!(desc, "Prefix: {}\n", x);
                }

                let mut has_commands = false;

                let commands = remove_aliases(&group.commands);
                let mut command_names = commands.keys().collect::<Vec<_>>();
                command_names.sort();

                for name in command_names {
                    let cmd = &commands[name];

                    if cmd.help_available && has_all_requirements(cmd, msg) {
                        let _ = write!(desc, "`{}`\n", name);
                        has_commands = true;
                    }
                }

                if has_commands {
                    e = e.field(|f| f.name(group_name).value(&desc));
                }
            }
            e
        })
    });

    Ok(())
}

/// Posts formatted text displaying each individual command group and its commands.
///
/// # Examples
///
/// Use the command with `exec_help`:
///
/// ```rust
/// # use serenity::prelude::*;
/// # struct Handler;
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler);
/// #
/// use serenity::framework::standard::{StandardFramework, help_commands};
///
/// client.with_framework(StandardFramework::new()
///     .command("help", |c| c.exec_help(help_commands::plain)));
/// ```
pub fn plain(_: &mut Context,
             msg: &Message,
             groups: HashMap<String, Arc<CommandGroup>>,
             args: Args)
             -> Result<(), CommandError> {
    if !args.is_empty() {
        let name = args.full();

        for (group_name, group) in groups {
            let mut found: Option<(&String, &Command)> = None;

            for (command_name, command) in &group.commands {
                let with_prefix = if let Some(ref prefix) = group.prefix {
                    format!("{} {}", prefix, command_name)
                } else {
                    command_name.to_string()
                };

                if name == with_prefix || name == *command_name {
                    match *command {
                        CommandOrAlias::Command(ref cmd) => {
                            if has_all_requirements(cmd, msg) {
                                found = Some((command_name, cmd));
                            }
                            else {
                                break;
                            }
                        },
                        CommandOrAlias::Alias(ref name) => {
                            let actual_command = &group.commands[name];

                            match *actual_command {
                                CommandOrAlias::Command(ref cmd) => {
                                    if has_all_requirements(cmd, msg) {
                                        found = Some((name, cmd));
                                    }
                                    else {
                                        break;
                                    }
                                },

                                CommandOrAlias::Alias(ref name) => {
                                    let _ = msg.channel_id.say(&format!("Did you mean {:?}?", name));
                                    return Ok(());
                                },
                            }
                        },
                    }
                }
            }

            if let Some((command_name, command)) = found {
                if !command.help_available {
                    let _ = msg.channel_id.say("**Error**: No help available.");
                    return Ok(());
                }

                let mut result = format!("**{}**\n", command_name);

                if !command.aliases.is_empty() {
                    let aliases = command.aliases.join("`, `");
                    let _ = write!(result, "**Aliases:** `{}`\n", aliases);
                }

                if let Some(ref desc) = command.desc {
                    let _ = write!(result, "**Description:** {}\n", desc);
                }

                if let Some(ref usage) = command.usage {
                    let _ = write!(result, "**Usage:** `{} {}`\n", command_name, usage);
                }

                if let Some(ref example) = command.example {
                    let _ = write!(result, "**Sample usage:** `{} {}`\n", command_name, example);
                }

                if group_name != "Ungrouped" {
                    let _ = write!(result, "**Group:** {}\n", group_name);
                }

                let only = if command.dm_only {
                    "Only in DM"
                } else if command.guild_only {
                    "Only in guilds"
                } else {
                    "In DM and guilds"
                };

                result.push_str("**Available:** ");
                result.push_str(only);
                result.push_str("\n");

                let _ = msg.channel_id.say(&result);

                return Ok(());
            }
        }

        let _ = msg.channel_id
            .say(&format!("**Error**: Command `{}` not found.", name));

        return Ok(());
    }

    let mut result = "**Commands**\nTo get help with an individual command, pass its \
                      name as an argument to this command.\n\n"
        .to_string();

    let mut group_names = groups.keys().collect::<Vec<_>>();
    group_names.sort();

    for group_name in group_names {
        let group = &groups[group_name];
        let mut group_help = String::new();

        let commands = remove_aliases(&group.commands);
        let mut command_names = commands.keys().collect::<Vec<_>>();
        command_names.sort();

        for name in command_names {
            let cmd = &commands[name];

            if cmd.help_available && has_all_requirements(cmd, msg) {
                let _ = write!(group_help, "`{}` ", name);
            }
        }

        if !group_help.is_empty() {
            let _ = write!(result, "**{}:** ", group_name);

            if let Some(ref x) = group.prefix {
                let _ = write!(result, "(prefix: `{}`): ", x);
            }

            result.push_str(&group_help);
            result.push('\n');
        }
    }

    let _ = msg.channel_id.say(&result);

    Ok(())
}
<|endoftext|>


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

#![deny(missing_docs)]

//! Helper crate for secure and convenient configuration of the Exonum nodes.
//!
//! `exonum-cli` supports multi-stage configuration process made with safety in mind. It involves
//! 4 steps (or stages) and allows to configure and run multiple blockchain nodes without
//! need in exchanging private keys between administrators.
//!
//! # How to Run the Network
//!
//! 1. Generate common (template) part of the nodes configuration using `generate-template` command.
//!   Generated `.toml` file must be spread among all the nodes and must be used in the following
//!   configuration step.
//! 2. Generate public and secret (private) parts of the node configuration using `generate-config`
//!   command. At this step, Exonum will generate master key from which consensus and service
//!   validator keys are derived. Master key is stored in the encrypted file. Consensus secret key
//!   is used for communications between the nodes, while service secret key is used
//!   mainly to sign transactions generated by the node. Both secret keys may be encrypted with a
//!   password. The public part of the node configuration must be spread among all nodes, while the
//!   secret part must be only accessible by the node administrator only.
//! 3. Generate final node configuration using `finalize` command. Exonum combines secret part of
//!   the node configuration with public configurations of every other node, producing a single
//!   configuration file with all the necessary node and network settings.
//! 4. Use `run` command and provide it with final node configuration file produced at the previous
//!   step. If the secret keys are protected with passwords, the user need to enter the password.
//!   Running node will automatically connect to other nodes in the network using IP addresses from
//!   public parts of the node configurations.
//!
//! ## Additional Commands
//!
//! `exonum-cli` also supports additional CLI commands for performing maintenance actions by node
//! administrators and easier debugging.
//!
//! * `run-dev` command automatically generates network configuration with a single node and runs
//! it. This command can be useful for fast testing of the services during development process.
//! * `maintenance` command contains only `clear-cache` functionality at the moment. It allows to
//! clear node's consensus messages cache to fix rare node out-of-sync issues.
//!
//! ## How to Extend Parameters
//!
//! `exonum-cli` allows to extend the list of the parameters for any command and even add new CLI
//! commands with arbitrary behavior. To do so, you need to implement a structure with a list of
//! additional parameters and use `flatten` macro attribute of [`serde`][serde] and
//! [`structopt`][structopt] libraries.
//!
//! ```ignore
//! #[derive(Serialize, Deserialize, StructOpt)]
//! struct MyRunCommand {
//!     #[serde(flatten)]
//!     #[structopt(flatten)]
//!     default: Run
//!     /// My awesome parameter
//!     secret_number: i32
//! }
//! ```
//!
//! You can also create own list of commands by implementing an enum with a similar principle:
//!
//! ```ignore
//! #[derive(StructOpt)]
//! enum MyCommands {
//!     #[structopt(name = "run")
//!     DefaultRun(Run),
//!     #[structopt(name = "my-run")
//!     MyAwesomeRun(MyRunCommand),
//! }
//! ```
//!
//! While implementing custom behavior for your commands, you may use
//! [`StandardResult`](./command/enum.StandardResult.html) enum for
//! accessing node configuration files created and filled by the standard Exonum commands.
//!
//! [serde]: https://crates.io/crates/serde
//! [structopt]: https://crates.io/crates/structopt

pub extern crate structopt;

use exonum::{
    blockchain::InstanceCollection,
    exonum_merkledb::{Database, RocksDB},
    node::Node,
    runtime::rust::ServiceFactory,
};
use exonum_supervisor::Supervisor;

use std::sync::Arc;

use crate::command::{Command, ExonumCommand, StandardResult};

pub mod command;
pub mod config;
pub mod io;
pub mod password;

/// Rust-specific node builder used for constructing a node with a list
/// of provided services.
#[derive(Debug, Default)]
pub struct NodeBuilder {
    services: Vec<Box<dyn ServiceFactory>>,
}

impl NodeBuilder {
    /// Creates new builder.
    pub fn new() -> Self {
        Self::default().with_service(Supervisor)
    }

    /// Adds new Rust service to the list of available services.
    pub fn with_service(mut self, service: impl Into<Box<dyn ServiceFactory>>) -> Self {
        self.services.push(service.into());
        self
    }

    /// Configures the node using parameters provided by user from stdin and then runs it.
    ///
    /// Only Rust runtime is enabled.
    pub fn run(self) -> Result<(), failure::Error> {
        let command = Command::from_args();
        if let StandardResult::Run(run_config) = command.execute()? {
            let db_options = &run_config.node_config.database;
            let database: Arc<dyn Database> =
                Arc::new(RocksDB::open(run_config.db_path, db_options)?);

            let node_config_path = run_config.node_config_path.to_string_lossy().to_string();
            let node = Node::new(
                database,
                self.services.into_iter().map(InstanceCollection::new),
                run_config.node_config,
                Some(node_config_path),
            );

            node.run()
        } else {
            Ok(())
        }
    }
}
<commit_msg>Fix supervisor creation (#1490)<commit_after>// Copyright 2019 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! Helper crate for secure and convenient configuration of the Exonum nodes.
//!
//! `exonum-cli` supports multi-stage configuration process made with safety in mind. It involves
//! 4 steps (or stages) and allows to configure and run multiple blockchain nodes without
//! need in exchanging private keys between administrators.
//!
//! # How to Run the Network
//!
//! 1. Generate common (template) part of the nodes configuration using `generate-template` command.
//!   Generated `.toml` file must be spread among all the nodes and must be used in the following
//!   configuration step.
//! 2. Generate public and secret (private) parts of the node configuration using `generate-config`
//!   command. At this step, Exonum will generate master key from which consensus and service
//!   validator keys are derived. Master key is stored in the encrypted file. Consensus secret key
//!   is used for communications between the nodes, while service secret key is used
//!   mainly to sign transactions generated by the node. Both secret keys may be encrypted with a
//!   password. The public part of the node configuration must be spread among all nodes, while the
//!   secret part must be only accessible by the node administrator only.
//! 3. Generate final node configuration using `finalize` command. Exonum combines secret part of
//!   the node configuration with public configurations of every other node, producing a single
//!   configuration file with all the necessary node and network settings.
//! 4. Use `run` command and provide it with final node configuration file produced at the previous
//!   step. If the secret keys are protected with passwords, the user need to enter the password.
//!   Running node will automatically connect to other nodes in the network using IP addresses from
//!   public parts of the node configurations.
//!
//! ## Additional Commands
//!
//! `exonum-cli` also supports additional CLI commands for performing maintenance actions by node
//! administrators and easier debugging.
//!
//! * `run-dev` command automatically generates network configuration with a single node and runs
//! it. This command can be useful for fast testing of the services during development process.
//! * `maintenance` command contains only `clear-cache` functionality at the moment. It allows to
//! clear node's consensus messages cache to fix rare node out-of-sync issues.
//!
//! ## How to Extend Parameters
//!
//! `exonum-cli` allows to extend the list of the parameters for any command and even add new CLI
//! commands with arbitrary behavior. To do so, you need to implement a structure with a list of
//! additional parameters and use `flatten` macro attribute of [`serde`][serde] and
//! [`structopt`][structopt] libraries.
//!
//! ```ignore
//! #[derive(Serialize, Deserialize, StructOpt)]
//! struct MyRunCommand {
//!     #[serde(flatten)]
//!     #[structopt(flatten)]
//!     default: Run
//!     /// My awesome parameter
//!     secret_number: i32
//! }
//! ```
//!
//! You can also create own list of commands by implementing an enum with a similar principle:
//!
//! ```ignore
//! #[derive(StructOpt)]
//! enum MyCommands {
//!     #[structopt(name = "run")
//!     DefaultRun(Run),
//!     #[structopt(name = "my-run")
//!     MyAwesomeRun(MyRunCommand),
//! }
//! ```
//!
//! While implementing custom behavior for your commands, you may use
//! [`StandardResult`](./command/enum.StandardResult.html) enum for
//! accessing node configuration files created and filled by the standard Exonum commands.
//!
//! [serde]: https://crates.io/crates/serde
//! [structopt]: https://crates.io/crates/structopt

pub extern crate structopt;

use exonum::{
    blockchain::InstanceCollection,
    exonum_merkledb::{Database, RocksDB},
    node::Node,
    runtime::rust::ServiceFactory,
};
use exonum_supervisor::Supervisor;

use std::sync::Arc;

use crate::command::{Command, ExonumCommand, StandardResult};

pub mod command;
pub mod config;
pub mod io;
pub mod password;

/// Rust-specific node builder used for constructing a node with a list
/// of provided services.
#[derive(Debug, Default)]
pub struct NodeBuilder {
    services: Vec<Box<dyn ServiceFactory>>,
}

impl NodeBuilder {
    /// Creates new builder.
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds new Rust service to the list of available services.
    pub fn with_service(mut self, service: impl Into<Box<dyn ServiceFactory>>) -> Self {
        self.services.push(service.into());
        self
    }

    fn builtin_services(&self) -> Vec<InstanceCollection> {
        // Supervisor service is enabled by default.
        vec![InstanceCollection::from(Supervisor)]
    }

    /// Configures the node using parameters provided by user from stdin and then runs it.
    ///
    /// Only Rust runtime is enabled.
    pub fn run(self) -> Result<(), failure::Error> {
        let command = Command::from_args();

        let services = self
            .builtin_services()
            .into_iter()
            .chain(self.services.into_iter().map(InstanceCollection::new));

        if let StandardResult::Run(run_config) = command.execute()? {
            let db_options = &run_config.node_config.database;
            let database: Arc<dyn Database> =
                Arc::new(RocksDB::open(run_config.db_path, db_options)?);

            let node_config_path = run_config.node_config_path.to_string_lossy().to_string();
            let node = Node::new(
                database,
                services,
                run_config.node_config,
                Some(node_config_path),
            );

            node.run()
        } else {
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate kernlog;

use std::env;
use std::process::{Command, Stdio};
use std::io::Write;

macro_rules! try_log {
    ($exp:expr) => {
        match $exp {
            Ok(v) => v,
            Err(e) => { warn!("{}", e); return; }
        }
    }
}

fn get_systemd_unit_property(unit: &str, prop: &str) -> String {
    String::from_utf8_lossy(&try_log!(Command::new("systemctl")
                            .arg("show")
                            .arg(unit)
                            .arg("--property")
                            .arg(prop)
                            .output())
                            .stdout[prop.len() + 1..])
        .trim_right_matches('\n')
        .to_owned()
}

fn main() {
    kernlog::init().unwrap();

    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = get_systemd_unit_property(&*unit, "User");
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = get_systemd_unit_property(&*unit, "Environment");
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

    if user.len() == 0 {
        return;
    }

    let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
        .arg("-n")
        .output())
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = try_log!(Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn());

    if let Some(ref mut stdin) = mailer.stdin {
        try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
                 stdin.write_all(&*status.stdout)));
    }

    mailer.wait().unwrap();
}
<commit_msg>print errors to stdout for journal<commit_after>
use std::env;
use std::process::{Command, Stdio};
use std::io::Write;

macro_rules! try_log {
    ($exp:expr) => {
        match $exp {
            Ok(v) => v,
            Err(e) => { println!("<3>{}", e); return; }
        }
    }
}

fn get_systemd_unit_property(unit: &str, prop: &str) -> String {
    String::from_utf8_lossy(&try_log!(Command::new("systemctl")
                            .arg("show")
                            .arg(unit)
                            .arg("--property")
                            .arg(prop)
                            .output())
                            .stdout[prop.len() + 1..])
        .trim_right_matches('\n')
        .to_owned()
}

fn main() {
    kernlog::init().unwrap();

    let unit = match env::args().nth(1) {
        Some(unit) => unit,
        None => {
            println!("Usage: mail-on-failure <unit>");
            return;
        }
    };

    let mut user = get_systemd_unit_property(&*unit, "User");
    if user.len() == 0 {
        user = "root".to_owned();
    }

    let job_env = get_systemd_unit_property(&*unit, "Environment");
    for pair in job_env.split(' ') {
        let mut p = pair.splitn(2, '=');
        if let (Some(name), Some(value)) = (p.next(), p.next()) {
            if name == "MAILTO" {
                user = value.to_owned();
                break;
            }
        }
    }

    if user.len() == 0 {
        return;
    }

    let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
        .arg("-n")
        .output())
        .stdout[..])
        .trim_right_matches('\n')
        .to_owned();

    if hostname.len() == 0 {
        hostname = "localhost".to_owned();
    }

    let mut head = String::new();
    head.push_str("From: root (systemd-cron)\nTo: ");
    head.push_str(&*user);
    head.push_str("\nSubject: [");
    head.push_str(&*hostname);
    head.push_str("] job ");
    head.push_str(&*unit);
    head.push_str(r###" failed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Auto-Submitted: auto-generated

"###);

    let status = Command::new("systemctl")
        .arg("status")
        .arg(&*unit)
        .output()
        .unwrap();

    let mut mailer = try_log!(Command::new("sendmail")
        .arg("-i")
        .arg("-B8BITMIME")
        .arg(&*user)
        .stdin(Stdio::piped())
        .spawn());

    if let Some(ref mut stdin) = mailer.stdin {
        try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
                 stdin.write_all(&*status.stdout)));
    }

    mailer.wait().unwrap();
}
<|endoftext|>