<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/. */

//! Generic implementations of some DOM APIs so they can be shared between Servo
//! and Gecko.

use Atom;
use context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{Combinator, Component, LocalName};
use smallvec::SmallVec;
use std::borrow::Borrow;

/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
    element: &E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> bool
where
    E: Element,
{
    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        None,
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());
    matching::matches_selector_list(selector_list, element, &mut context)
}

/// <https://dom.spec.whatwg.org/#dom-element-closest>
pub fn element_closest<E>(
    element: E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> Option<E>
where
    E: Element,
{
    let mut nth_index_cache = NthIndexCache::default();

    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());

    let mut current = Some(element);
    while let Some(element) = current.take() {
        if matching::matches_selector_list(selector_list, &element, &mut context) {
            return Some(element);
        }
        current = element.parent_element();
    }

    return None;
}

/// A selector query abstraction, in order to be generic over QuerySelector and
/// QuerySelectorAll.
pub trait SelectorQuery<E: TElement> {
    /// The output of the query.
    type Output;

    /// Whether the query should stop after the first element has been matched.
    fn should_stop_after_first_match() -> bool;

    /// Append an element matching after the first query.
    fn append_element(output: &mut Self::Output, element: E);

    /// Returns true if the output is empty.
    fn is_empty(output: &Self::Output) -> bool;
}

/// The result of a querySelectorAll call.
pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;

/// A query for all the elements in a subtree.
pub struct QueryAll;

impl<E: TElement> SelectorQuery<E> for QueryAll {
    type Output = QuerySelectorAllResult<E>;

    fn should_stop_after_first_match() -> bool { false }

    fn append_element(output: &mut Self::Output, element: E) {
        output.push(element);
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_empty()
    }
}

/// A query for the first in-tree match of all the elements in a subtree.
pub struct QueryFirst;

impl<E: TElement> SelectorQuery<E> for QueryFirst {
    type Output = Option<E>;

    fn should_stop_after_first_match() -> bool { true }

    fn append_element(output: &mut Self::Output, element: E) {
        if output.is_none() {
            *output = Some(element)
        }
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_none()
    }
}

struct QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    results: &'a mut Q::Output,
    matching_context: MatchingContext<'a, E::Impl>,
    selector_list: &'a SelectorList<E::Impl>,
}

impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    fn light_tree_only(&self) -> bool { true }

    fn collect_invalidations(
        &mut self,
        element: E,
        self_invalidations: &mut InvalidationVector<'a>,
        descendant_invalidations: &mut InvalidationVector<'a>,
        _sibling_invalidations: &mut InvalidationVector<'a>,
    ) -> bool {
        // TODO(emilio): If the element is not a root element, and
        // selector_list has any descendant combinator, we need to do extra work
        // in order to handle properly things like:
        //
        //

        //

        //

        //

        //

        //
        // b.querySelector('#a div'); // Should return "c".
        //
        // For now, assert it's a root element.
        debug_assert!(element.parent_element().is_none());

        let target_vector =
            if self.matching_context.scope_element.is_some() {
                descendant_invalidations
            } else {
                self_invalidations
            };

        for selector in self.selector_list.0.iter() {
            target_vector.push(Invalidation::new(selector, 0))
        }

        false
    }

    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
        &mut self.matching_context
    }

    fn should_process_descendants(&mut self, _: E) -> bool {
        if Q::should_stop_after_first_match() {
            return Q::is_empty(&self.results)
        }

        true
    }

    fn invalidated_self(&mut self, e: E) {
        Q::append_element(self.results, e);
    }

    fn recursion_limit_exceeded(&mut self, _e: E) {}
    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
}

fn collect_all_elements<E, Q, F>(
    root: E::ConcreteNode,
    results: &mut Q::Output,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    for node in root.dom_descendants() {
        let element = match node.as_element() {
            Some(e) => e,
            None => continue,
        };

        if !filter(element) {
            continue;
        }

        Q::append_element(results, element);
        if Q::should_stop_after_first_match() {
            return;
        }
    }
}

/// Returns whether a given element is descendant of a given `root` node.
///
/// NOTE(emilio): if root == element, this returns false.
fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
where
    E: TElement,
{
    let mut current = element.as_node().parent_node();
    while let Some(n) = current.take() {
        if n == root {
            return true;
        }

        current = n.parent_node();
    }
    false
}

fn find_elements_with_id<E, Q, F>(
    root: E::ConcreteNode,
    id: &Atom,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
    if case_sensitivity == CaseSensitivity::CaseSensitive &&
       root.is_in_document()
    {
        let doc = root.owner_doc();
        if let Ok(elements) = doc.elements_with_id(id) {
            if root == doc.as_node() {
                for element in elements {
                    if !filter(*element) {
                        continue;
                    }

                    Q::append_element(results, *element);
                    if Q::should_stop_after_first_match() {
                        break;
                    }
                }
            } else {
                for element in elements {
                    if !element_is_descendant_of(*element, root) {
                        continue;
                    }

                    if !filter(*element) {
                        continue;
                    }

                    Q::append_element(results, *element);
                    if Q::should_stop_after_first_match() {
                        break;
                    }
                }
            }
        }
        return;
    }

    collect_all_elements::<E, Q, _>(root, results, |element| {
        element.has_id(id, case_sensitivity) && filter(element)
    });
}

/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
    root: E::ConcreteNode,
    component: &Component<E::Impl>,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        }
        Component::ID(ref id) => {
            find_elements_with_id::<E, Q, _>(
                root,
                id,
                results,
                quirks_mode,
                |_| true,
            )
        }
        Component::Class(ref class) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_class(class, case_sensitivity)
            })
        }
        Component::LocalName(LocalName { ref name, ref lower_name }) => {
            collect_all_elements::<E, Q, _>(root, results, |element| {
                if element.is_html_element_in_html_document() {
                    element.get_local_name() == lower_name.borrow()
                } else {
                    element.get_local_name() == name.borrow()
                }
            })
        }
        // TODO(emilio): More fast paths?
        _ => {
            return Err(())
        }
    }

    Ok(())
}

/// Fast paths for a given selector query.
///
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
/// replaced by HolyJit :)
fn query_selector_fast<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    // We need to return elements in document order, and reordering them
    // afterwards is kinda silly.
    if selector_list.0.len() > 1 {
        return Err(());
    }

    let selector = &selector_list.0[0];
    let quirks_mode = matching_context.quirks_mode();

    // Let's just care about the easy cases for now.
    if selector.len() == 1 {
        return query_selector_single_query::<E, Q>(
            root,
            selector.iter().next().unwrap(),
            results,
            quirks_mode,
        );
    }

    let mut iter = selector.iter();
    let mut combinator: Option<Combinator> = None;

    loop {
        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));

        for component in &mut iter {
            match *component {
                Component::ID(ref id) => {
                    if combinator.is_none() {
                        // In the rightmost compound, just find descendants of
                        // root that match the selector list with that id.
                        find_elements_with_id::<E, Q, _>(
                            root,
                            id,
                            results,
                            quirks_mode,
                            |e| {
                                matching::matches_selector_list(
                                    selector_list,
                                    &e,
                                    matching_context,
                                )
                            }
                        );

                        return Ok(());
                    }

                    // TODO(emilio): Find descendants of `root` that are
                    // descendants of an element with a given `id`.
                }
                _ => {},
            }
        }

        loop {
            let next_combinator = match iter.next_sequence() {
                None => return Err(()),
                Some(c) => c,
            };

            // We don't want to scan stuff affected by sibling combinators,
            // given we scan the subtree of elements with a given id (and we
            // don't want to care about scanning the siblings' subtrees).
            if next_combinator.is_sibling() {
                // Advance to the next combinator.
                for _ in &mut iter {}
                continue;
            }

            combinator = Some(next_combinator);
            break;
        }
    }
}

// Slow path for a given selector query.
fn query_selector_slow<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    collect_all_elements::<E, Q, _>(root, results, |element| {
        matching::matches_selector_list(selector_list, &element, matching_context)
    });
}

/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
pub fn query_selector<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    use invalidation::element::invalidator::TreeStyleInvalidator;

    let quirks_mode = root.owner_doc().quirks_mode();

    let mut nth_index_cache = NthIndexCache::default();
    let mut matching_context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );

    let root_element = root.as_element();
    matching_context.scope_element = root_element.map(|e| e.opaque());

    let fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        &mut matching_context,
    );

    if fast_result.is_ok() {
        return;
    }

    // Slow path: Use the invalidation machinery if we're a root, and tree
    // traversal otherwise.
    //
    // See the comment in collect_invalidations to see why only if we're a root.
    //
    // The invalidation mechanism is only useful in presence of combinators.
    //
    // We could do that check properly here, though checking the length of the
    // selectors is a good heuristic.
    let invalidation_may_be_useful =
        selector_list.0.iter().any(|s| s.len() > 1);

    if root_element.is_some() || !invalidation_may_be_useful {
        query_selector_slow::<E, Q>(
            root,
            selector_list,
            results,
            &mut matching_context,
        );
    } else {
        let mut processor = QuerySelectorProcessor::<E, Q> {
            results,
            matching_context,
            selector_list,
        };

        for node in root.dom_children() {
            if let Some(e) = node.as_element() {
                TreeStyleInvalidator::new(
                    e,
                    /* stack_limit_checker = */ None,
                    &mut processor,
                ).invalidate();
            }
        }
    }
}
<commit_msg>style: Extract a bit better the logic for finding elements with an ID under a subtree.<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/. */

//! Generic implementations of some DOM APIs so they can be shared between Servo
//! and Gecko.

use Atom;
use context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{Combinator, Component, LocalName};
use smallvec::SmallVec;
use std::borrow::Borrow;

/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
    element: &E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> bool
where
    E: Element,
{
    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        None,
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());
    matching::matches_selector_list(selector_list, element, &mut context)
}

/// <https://dom.spec.whatwg.org/#dom-element-closest>
pub fn element_closest<E>(
    element: E,
    selector_list: &SelectorList<E::Impl>,
    quirks_mode: QuirksMode,
) -> Option<E>
where
    E: Element,
{
    let mut nth_index_cache = NthIndexCache::default();

    let mut context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );
    context.scope_element = Some(element.opaque());

    let mut current = Some(element);
    while let Some(element) = current.take() {
        if matching::matches_selector_list(selector_list, &element, &mut context) {
            return Some(element);
        }
        current = element.parent_element();
    }

    return None;
}

/// A selector query abstraction, in order to be generic over QuerySelector and
/// QuerySelectorAll.
pub trait SelectorQuery<E: TElement> {
    /// The output of the query.
    type Output;

    /// Whether the query should stop after the first element has been matched.
    fn should_stop_after_first_match() -> bool;

    /// Append an element matching after the first query.
    fn append_element(output: &mut Self::Output, element: E);

    /// Returns true if the output is empty.
    fn is_empty(output: &Self::Output) -> bool;
}

/// The result of a querySelectorAll call.
pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;

/// A query for all the elements in a subtree.
pub struct QueryAll;

impl<E: TElement> SelectorQuery<E> for QueryAll {
    type Output = QuerySelectorAllResult<E>;

    fn should_stop_after_first_match() -> bool { false }

    fn append_element(output: &mut Self::Output, element: E) {
        output.push(element);
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_empty()
    }
}

/// A query for the first in-tree match of all the elements in a subtree.
pub struct QueryFirst;

impl<E: TElement> SelectorQuery<E> for QueryFirst {
    type Output = Option<E>;

    fn should_stop_after_first_match() -> bool { true }

    fn append_element(output: &mut Self::Output, element: E) {
        if output.is_none() {
            *output = Some(element)
        }
    }

    fn is_empty(output: &Self::Output) -> bool {
        output.is_none()
    }
}

struct QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    results: &'a mut Q::Output,
    matching_context: MatchingContext<'a, E::Impl>,
    selector_list: &'a SelectorList<E::Impl>,
}

impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
where
    E: TElement + 'a,
    Q: SelectorQuery<E>,
    Q::Output: 'a,
{
    fn light_tree_only(&self) -> bool { true }

    fn collect_invalidations(
        &mut self,
        element: E,
        self_invalidations: &mut InvalidationVector<'a>,
        descendant_invalidations: &mut InvalidationVector<'a>,
        _sibling_invalidations: &mut InvalidationVector<'a>,
    ) -> bool {
        // TODO(emilio): If the element is not a root element, and
        // selector_list has any descendant combinator, we need to do extra work
        // in order to handle properly things like:
        //
        //

        //

        //

        //

        //

        //
        // b.querySelector('#a div'); // Should return "c".
        //
        // For now, assert it's a root element.
        debug_assert!(element.parent_element().is_none());

        let target_vector =
            if self.matching_context.scope_element.is_some() {
                descendant_invalidations
            } else {
                self_invalidations
            };

        for selector in self.selector_list.0.iter() {
            target_vector.push(Invalidation::new(selector, 0))
        }

        false
    }

    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
        &mut self.matching_context
    }

    fn should_process_descendants(&mut self, _: E) -> bool {
        if Q::should_stop_after_first_match() {
            return Q::is_empty(&self.results)
        }

        true
    }

    fn invalidated_self(&mut self, e: E) {
        Q::append_element(self.results, e);
    }

    fn recursion_limit_exceeded(&mut self, _e: E) {}
    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
}

fn collect_all_elements<E, Q, F>(
    root: E::ConcreteNode,
    results: &mut Q::Output,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    for node in root.dom_descendants() {
        let element = match node.as_element() {
            Some(e) => e,
            None => continue,
        };

        if !filter(element) {
            continue;
        }

        Q::append_element(results, element);
        if Q::should_stop_after_first_match() {
            return;
        }
    }
}

/// Returns whether a given element is descendant of a given `root` node.
///
/// NOTE(emilio): if root == element, this returns false.
fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
where
    E: TElement,
{
    let mut current = element.as_node().parent_node();
    while let Some(n) = current.take() {
        if n == root {
            return true;
        }

        current = n.parent_node();
    }
    false
}

/// Execute `callback` on each element with a given `id` under `root`.
///
/// If `callback` returns false, iteration will stop immediately.
fn each_element_with_id_under<E, F>(
    root: E::ConcreteNode,
    id: &Atom,
    quirks_mode: QuirksMode,
    mut callback: F,
)
where
    E: TElement,
    F: FnMut(E) -> bool,
{
    let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
    if case_sensitivity == CaseSensitivity::CaseSensitive &&
       root.is_in_document()
    {
        let doc = root.owner_doc();
        if let Ok(elements) = doc.elements_with_id(id) {
            if root == doc.as_node() {
                for element in elements {
                    if !callback(*element) {
                        return;
                    }
                }
            } else {
                for element in elements {
                    if !element_is_descendant_of(*element, root) {
                        continue;
                    }

                    if !callback(*element) {
                        return;
                    }
                }
            }
        }
        return;
    }

    for node in root.dom_descendants() {
        let element = match node.as_element() {
            Some(e) => e,
            None => continue,
        };

        if !element.has_id(id, case_sensitivity) {
            continue;
        }

        if !callback(element) {
            return;
        }
    }
}

fn collect_elements_with_id<E, Q, F>(
    root: E::ConcreteNode,
    id: &Atom,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
    mut filter: F,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
    F: FnMut(E) -> bool,
{
    each_element_with_id_under::<E, _>(root, id, quirks_mode, |element| {
        if !filter(element) {
            return true;
        }

        Q::append_element(results, element);
        return !Q::should_stop_after_first_match()
    })
}

/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
    root: E::ConcreteNode,
    component: &Component<E::Impl>,
    results: &mut Q::Output,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        }
        Component::ID(ref id) => {
            collect_elements_with_id::<E, Q, _>(
                root,
                id,
                results,
                quirks_mode,
                |_| true,
            )
        }
        Component::Class(ref class) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_class(class, case_sensitivity)
            })
        }
        Component::LocalName(LocalName { ref name, ref lower_name }) => {
            collect_all_elements::<E, Q, _>(root, results, |element| {
                if element.is_html_element_in_html_document() {
                    element.get_local_name() == lower_name.borrow()
                } else {
                    element.get_local_name() == name.borrow()
                }
            })
        }
        // TODO(emilio): More fast paths?
        _ => {
            return Err(())
        }
    }

    Ok(())
}

/// Fast paths for a given selector query.
///
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
/// replaced by HolyJit :)
fn query_selector_fast<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    // We need to return elements in document order, and reordering them
    // afterwards is kinda silly.
    if selector_list.0.len() > 1 {
        return Err(());
    }

    let selector = &selector_list.0[0];
    let quirks_mode = matching_context.quirks_mode();

    // Let's just care about the easy cases for now.
    if selector.len() == 1 {
        return query_selector_single_query::<E, Q>(
            root,
            selector.iter().next().unwrap(),
            results,
            quirks_mode,
        );
    }

    let mut iter = selector.iter();
    let mut combinator: Option<Combinator> = None;

    loop {
        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));

        for component in &mut iter {
            match *component {
                Component::ID(ref id) => {
                    if combinator.is_none() {
                        // In the rightmost compound, just find descendants of
                        // root that match the selector list with that id.
                        collect_elements_with_id::<E, Q, _>(
                            root,
                            id,
                            results,
                            quirks_mode,
                            |e| {
                                matching::matches_selector_list(
                                    selector_list,
                                    &e,
                                    matching_context,
                                )
                            }
                        );

                        return Ok(());
                    }

                    // TODO(emilio): Find descendants of `root` that are
                    // descendants of an element with a given `id`.
                }
                _ => {},
            }
        }

        loop {
            let next_combinator = match iter.next_sequence() {
                None => return Err(()),
                Some(c) => c,
            };

            // We don't want to scan stuff affected by sibling combinators,
            // given we scan the subtree of elements with a given id (and we
            // don't want to care about scanning the siblings' subtrees).
            if next_combinator.is_sibling() {
                // Advance to the next combinator.
                for _ in &mut iter {}
                continue;
            }

            combinator = Some(next_combinator);
            break;
        }
    }
}

// Slow path for a given selector query.
fn query_selector_slow<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
    matching_context: &mut MatchingContext<E::Impl>,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    collect_all_elements::<E, Q, _>(root, results, |element| {
        matching::matches_selector_list(selector_list, &element, matching_context)
    });
}

/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
pub fn query_selector<E, Q>(
    root: E::ConcreteNode,
    selector_list: &SelectorList<E::Impl>,
    results: &mut Q::Output,
)
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    use invalidation::element::invalidator::TreeStyleInvalidator;

    let quirks_mode = root.owner_doc().quirks_mode();

    let mut nth_index_cache = NthIndexCache::default();
    let mut matching_context = MatchingContext::new(
        MatchingMode::Normal,
        None,
        Some(&mut nth_index_cache),
        quirks_mode,
    );

    let root_element = root.as_element();
    matching_context.scope_element = root_element.map(|e| e.opaque());

    let fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        &mut matching_context,
    );

    if fast_result.is_ok() {
        return;
    }

    // Slow path: Use the invalidation machinery if we're a root, and tree
    // traversal otherwise.
    //
    // See the comment in collect_invalidations to see why only if we're a root.
    //
    // The invalidation mechanism is only useful in presence of combinators.
    //
    // We could do that check properly here, though checking the length of the
    // selectors is a good heuristic.
    let invalidation_may_be_useful =
        selector_list.0.iter().any(|s| s.len() > 1);

    if root_element.is_some() || !invalidation_may_be_useful {
        query_selector_slow::<E, Q>(
            root,
            selector_list,
            results,
            &mut matching_context,
        );
    } else {
        let mut processor = QuerySelectorProcessor::<E, Q> {
            results,
            matching_context,
            selector_list,
        };

        for node in root.dom_children() {
            if let Some(e) = node.as_element() {
                TreeStyleInvalidator::new(
                    e,
                    /* stack_limit_checker = */ None,
                    &mut processor,
                ).invalidate();
            }
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code))]

#![feature(collections)]
#![feature(io)]
#![feature(old_io)]

extern crate document;

use std::env;
use std::cmp::min;
use std::fs::File;
use std::io::{Read,Cursor};
use std::old_io::stdio;

use document::parser::Parser;

fn pretty_error(xml: &str, position: usize) -> &str {
    let s = &xml[position..];
    let l = s.chars().count();
    s.slice_chars(0, min(l, 15))
}

fn process_input<R>(input: R)
    where R: Read
{
    let mut input = input;
    let mut data = String::new();
    match input.read_to_string(&mut data) {
        Ok(x) => x,
        Err(x) => panic!("Can't read: {}", x),
    };

    let p = Parser::new();

    let package = match p.parse(&data) {
        Ok(d) => d,
        Err(point) => panic!("Unable to parse: {}", pretty_error(&data, point)),
    };

    // TODO: Remove buffering when std::io gets stdout
    let mut out = Vec::new();
    let d = package.as_document();
    document::writer::format_document(&d, &mut out).ok().expect("I can't output");

    stdio::stdout_raw().write_all(&out).unwrap();
}

fn main() {
    let mut args: Vec<_> = env::args().collect();

    let filename = args.remove(1);

    if filename == "-" {
        let data = stdio::stdin_raw().read_to_end().unwrap();
        process_input(Cursor::new(data));
    } else {
        let file = File::open(&filename).unwrap();
        process_input(file);
    }
}
<commit_msg>New IO system has stdin / stdout again<commit_after>#![cfg_attr(test, allow(dead_code))]

#![feature(collections)]
#![feature(io)]

extern crate document;

use std::env;
use std::cmp::min;
use std::fs::File;
use std::io::{self,Read,Write};

use document::parser::Parser;

fn pretty_error(xml: &str, position: usize) -> &str {
    let s = &xml[position..];
    let l = s.chars().count();
    s.slice_chars(0, min(l, 15))
}

fn process_input<R>(input: R)
    where R: Read
{
    let mut input = input;
    let mut data = String::new();

    if let Err(x) = input.read_to_string(&mut data) {
        panic!("Can't read: {}", x);
    }

    let p = Parser::new();

    let package = match p.parse(&data) {
        Ok(d) => d,
        Err(point) => panic!("Unable to parse: {}", pretty_error(&data, point)),
    };

    let mut out = io::stdout();
    let d = package.as_document();

    document::writer::format_document(&d, &mut out).ok().expect("I can't output");
    // Remove when we move back to stdout_raw + buffer or when stdout flushed at program exit
    out.flush().unwrap();
}

fn main() {
    let mut args: Vec<_> = env::args().collect();

    let filename = args.remove(1);

    if filename == "-" {
        process_input(io::stdin());
    } else {
        let file = File::open(&filename).unwrap();
        process_input(file);
    }
}
<|endoftext|>


<commit_before>use crate::{
    connection_closer::ConnectionCloser,
    error_handler::ErrorHandler,
    executor::Executor,
    frames::Frames,
    id_sequence::IdSequence,
    internal_rpc::InternalRPCHandle,
    protocol::{AMQPClass, AMQPError, AMQPHardError},
    socket_state::SocketStateHandle,
    topology::ChannelDefinition,
    BasicProperties, Channel, ChannelState, Configuration, ConnectionState, ConnectionStatus,
    Error, Promise, Result,
};
use amq_protocol::frame::{AMQPFrame, ProtocolVersion};
use log::{debug, error, log_enabled, trace, Level::Trace};
use parking_lot::Mutex;
use std::{collections::HashMap, fmt, sync::Arc};

#[derive(Clone)]
pub(crate) struct Channels {
    inner: Arc<Mutex<Inner>>,
    connection_status: ConnectionStatus,
    internal_rpc: InternalRPCHandle,
    executor: Arc<dyn Executor>,
    frames: Frames,
    error_handler: ErrorHandler,
}

impl Channels {
    pub(crate) fn new(
        configuration: Configuration,
        connection_status: ConnectionStatus,
        waker: SocketStateHandle,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
    ) -> Self {
        Self {
            inner: Arc::new(Mutex::new(Inner::new(configuration, waker))),
            connection_status,
            internal_rpc,
            executor,
            frames,
            error_handler: ErrorHandler::default(),
        }
    }

    pub(crate) fn create(&self, connection_closer: Arc<ConnectionCloser>) -> Result<Channel> {
        self.inner.lock().create(
            self.connection_status.clone(),
            self.internal_rpc.clone(),
            self.frames.clone(),
            self.executor.clone(),
            connection_closer,
        )
    }

    pub(crate) fn create_zero(&self) {
        self.inner
            .lock()
            .create_channel(
                0,
                self.connection_status.clone(),
                self.internal_rpc.clone(),
                self.frames.clone(),
                self.executor.clone(),
                None,
            )
            .set_state(ChannelState::Connected);
    }

    pub(crate) fn get(&self, id: u16) -> Option<Channel> {
        self.inner.lock().channels.get(&id).cloned()
    }

    pub(crate) fn remove(&self, id: u16, error: Error) -> Result<()> {
        self.frames.clear_expected_replies(id, error);
        if self.inner.lock().channels.remove(&id).is_some() {
            Ok(())
        } else {
            Err(Error::InvalidChannel(id))
        }
    }

    pub(crate) fn receive_method(&self, id: u16, method: AMQPClass) -> Result<()> {
        self.get(id)
            .map(|channel| channel.receive_method(method))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn handle_content_header_frame(
        &self,
        id: u16,
        class_id: u16,
        size: u64,
        properties: BasicProperties,
    ) -> Result<()> {
        self.get(id)
            .map(|channel| channel.handle_content_header_frame(class_id, size, properties))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn handle_body_frame(&self, id: u16, payload: Vec<u8>) -> Result<()> {
        self.get(id)
            .map(|channel| channel.handle_body_frame(payload))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn set_connection_closing(&self) {
        self.connection_status.set_state(ConnectionState::Closing);
        for channel in self.inner.lock().channels.values() {
            channel.set_state(ChannelState::Closing);
        }
    }

    pub(crate) fn set_connection_closed(&self, error: Error) -> Result<()> {
        self.connection_status.set_state(ConnectionState::Closed);
        self.inner
            .lock()
            .channels
            .drain()
            .map(|(id, channel)| {
                self.frames.clear_expected_replies(id, error.clone());
                channel.set_state(ChannelState::Closed);
                channel.error_publisher_confirms(error.clone());
                channel.cancel_consumers()
            })
            .fold(Ok(()), Result::and)
    }

    pub(crate) fn set_connection_error(&self, error: Error) -> Result<()> {
        if let ConnectionState::Error = self.connection_status.state() {
            return Ok(());
        }

        error!("Connection error: {}", error);
        self.connection_status.set_state(ConnectionState::Error);
        self.frames.drop_pending(error.clone());
        self.error_handler.on_error(error.clone());
        self.inner
            .lock()
            .channels
            .drain()
            .map(|(id, channel)| {
                self.frames.clear_expected_replies(id, error.clone());
                channel.set_state(ChannelState::Error);
                channel.error_publisher_confirms(error.clone());
                channel.error_consumers(error.clone())
            })
            .fold(Ok(()), Result::and)
    }

    pub(crate) fn flow(&self) -> bool {
        self.inner
            .lock()
            .channels
            .values()
            .all(|c| c.status().flow())
    }

    pub(crate) fn send_heartbeat(&self) -> Result<()> {
        debug!("send heartbeat");

        self.get(0)
            .map(|channel0| {
                let (promise, resolver) = Promise::new();

                if log_enabled!(Trace) {
                    promise.set_marker("Heartbeat".into());
                }

                channel0.send_frame(AMQPFrame::Heartbeat(0), resolver, None);
                self.internal_rpc.register_internal_future(promise)
            })
            .unwrap_or_else(|| {
                Err(Error::InvalidConnectionState(
                    self.connection_status.state(),
                ))
            })
    }

    pub(crate) fn handle_frame(&self, f: AMQPFrame) -> Result<()> {
        if let Err(err) = self.do_handle_frame(f) {
            self.set_connection_error(err.clone())?;
            Err(err)
        } else {
            Ok(())
        }
    }

    fn do_handle_frame(&self, f: AMQPFrame) -> Result<()> {
        trace!("will handle frame: {:?}", f);
        match f {
            AMQPFrame::ProtocolHeader(version) => {
                error!(
                    "we asked for AMQP {} but the server only supports AMQP {}",
                    ProtocolVersion::amqp_0_9_1(),
                    version
                );
                let error = Error::InvalidProtocolVersion(version);
                if let Some(resolver) = self.connection_status.connection_resolver() {
                    resolver.swear(Err(error.clone()));
                }
                return Err(error);
            }
            AMQPFrame::Method(channel_id, method) => {
                self.receive_method(channel_id, method)?;
            }
            AMQPFrame::Heartbeat(channel_id) => {
                if channel_id == 0 {
                    debug!("received heartbeat from server");
                } else {
                    error!("received invalid heartbeat on channel {}", channel_id);
                    let error = AMQPError::new(
                        AMQPHardError::FRAMEERROR.into(),
                        format!("heartbeat frame received on channel {}", channel_id).into(),
                    );
                    if let Some(Err(error)) = self.get(0).map(|channel0| {
                        self.internal_rpc
                            .register_internal_future(channel0.connection_close(
                                error.get_id(),
                                error.get_message().as_str(),
                                0,
                                0,
                            ))
                    }) {
                        return Err(error);
                    }
                    return Err(Error::ProtocolError(error));
                }
            }
            AMQPFrame::Header(channel_id, class_id, header) => {
                if channel_id == 0 {
                    error!("received content header on channel {}", channel_id);
                    let error = AMQPError::new(
                        AMQPHardError::CHANNELERROR.into(),
                        format!("content header frame received on channel {}", channel_id).into(),
                    );
                    if let Some(Err(error)) = self.get(0).map(|channel0| {
                        self.internal_rpc
                            .register_internal_future(channel0.connection_close(
                                error.get_id(),
                                error.get_message().as_str(),
                                class_id,
                                0,
                            ))
                    }) {
                        return Err(error);
                    }
                    return Err(Error::ProtocolError(error));
                } else {
                    self.handle_content_header_frame(
                        channel_id,
                        class_id,
                        header.body_size,
                        header.properties,
                    )?;
                }
            }
            AMQPFrame::Body(channel_id, payload) => {
                self.handle_body_frame(channel_id, payload)?;
            }
        }
        Ok(())
    }

    pub(crate) fn set_error_handler<E: FnMut(Error) + Send + 'static>(&self, handler: E) {
        self.error_handler.set_handler(handler);
    }

    pub(crate) fn topology(&self) -> Vec<ChannelDefinition> {
        self.inner
            .lock()
            .channels
            .values()
            .map(Channel::topology)
            .collect()
    }
}

impl fmt::Debug for Channels {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Channels");
        if let Some(inner) = self.inner.try_lock() {
            debug
                .field("channels", &inner.channels.values())
                .field("channel_id", &inner.channel_id)
                .field("configuration", &inner.configuration);
        }
        debug
            .field("frames", &self.frames)
            .field("executor", &self.executor)
            .field("connection_status", &self.connection_status)
            .field("error_handler", &self.error_handler)
            .finish()
    }
}

struct Inner {
    channels: HashMap<u16, Channel>,
    channel_id: IdSequence<u16>,
    configuration: Configuration,
    waker: SocketStateHandle,
}

impl Inner {
    fn new(configuration: Configuration, waker: SocketStateHandle) -> Self {
        Self {
            channels: HashMap::default(),
            channel_id: IdSequence::new(false),
            configuration,
            waker,
        }
    }

    fn create_channel(
        &mut self,
        id: u16,
        connection_status: ConnectionStatus,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
        connection_closer: Option<Arc<ConnectionCloser>>,
    ) -> Channel {
        debug!("create channel with id {}", id);
        let channel = Channel::new(
            id,
            self.configuration.clone(),
            connection_status,
            self.waker.clone(),
            internal_rpc,
            frames,
            executor,
            connection_closer,
        );
        self.channels.insert(id, channel.clone_internal());
        channel
    }

    fn create(
        &mut self,
        connection_status: ConnectionStatus,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
        connection_closer: Arc<ConnectionCloser>,
    ) -> Result<Channel> {
        debug!("create channel");
        self.channel_id.set_max(self.configuration.channel_max());
        let first_id = self.channel_id.next();
        let mut id = first_id;
        let mut met_first_id = false;
        loop {
            if id == first_id {
                if met_first_id {
                    break;
                }
                met_first_id = true;
            }
            if !self.channels.contains_key(&id) {
                return Ok(self.create_channel(
                    id,
                    connection_status,
                    internal_rpc,
                    frames,
                    executor,
                    Some(connection_closer),
                ));
            }
            id = self.channel_id.next();
        }
        Err(Error::ChannelsLimitReached)
    }
}
<commit_msg>topology: skip channel 0<commit_after>use crate::{
    connection_closer::ConnectionCloser,
    error_handler::ErrorHandler,
    executor::Executor,
    frames::Frames,
    id_sequence::IdSequence,
    internal_rpc::InternalRPCHandle,
    protocol::{AMQPClass, AMQPError, AMQPHardError},
    socket_state::SocketStateHandle,
    topology::ChannelDefinition,
    BasicProperties, Channel, ChannelState, Configuration, ConnectionState, ConnectionStatus,
    Error, Promise, Result,
};
use amq_protocol::frame::{AMQPFrame, ProtocolVersion};
use log::{debug, error, log_enabled, trace, Level::Trace};
use parking_lot::Mutex;
use std::{collections::HashMap, fmt, sync::Arc};

#[derive(Clone)]
pub(crate) struct Channels {
    inner: Arc<Mutex<Inner>>,
    connection_status: ConnectionStatus,
    internal_rpc: InternalRPCHandle,
    executor: Arc<dyn Executor>,
    frames: Frames,
    error_handler: ErrorHandler,
}

impl Channels {
    pub(crate) fn new(
        configuration: Configuration,
        connection_status: ConnectionStatus,
        waker: SocketStateHandle,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
    ) -> Self {
        Self {
            inner: Arc::new(Mutex::new(Inner::new(configuration, waker))),
            connection_status,
            internal_rpc,
            executor,
            frames,
            error_handler: ErrorHandler::default(),
        }
    }

    pub(crate) fn create(&self, connection_closer: Arc<ConnectionCloser>) -> Result<Channel> {
        self.inner.lock().create(
            self.connection_status.clone(),
            self.internal_rpc.clone(),
            self.frames.clone(),
            self.executor.clone(),
            connection_closer,
        )
    }

    pub(crate) fn create_zero(&self) {
        self.inner
            .lock()
            .create_channel(
                0,
                self.connection_status.clone(),
                self.internal_rpc.clone(),
                self.frames.clone(),
                self.executor.clone(),
                None,
            )
            .set_state(ChannelState::Connected);
    }

    pub(crate) fn get(&self, id: u16) -> Option<Channel> {
        self.inner.lock().channels.get(&id).cloned()
    }

    pub(crate) fn remove(&self, id: u16, error: Error) -> Result<()> {
        self.frames.clear_expected_replies(id, error);
        if self.inner.lock().channels.remove(&id).is_some() {
            Ok(())
        } else {
            Err(Error::InvalidChannel(id))
        }
    }

    pub(crate) fn receive_method(&self, id: u16, method: AMQPClass) -> Result<()> {
        self.get(id)
            .map(|channel| channel.receive_method(method))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn handle_content_header_frame(
        &self,
        id: u16,
        class_id: u16,
        size: u64,
        properties: BasicProperties,
    ) -> Result<()> {
        self.get(id)
            .map(|channel| channel.handle_content_header_frame(class_id, size, properties))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn handle_body_frame(&self, id: u16, payload: Vec<u8>) -> Result<()> {
        self.get(id)
            .map(|channel| channel.handle_body_frame(payload))
            .unwrap_or_else(|| Err(Error::InvalidChannel(id)))
    }

    pub(crate) fn set_connection_closing(&self) {
        self.connection_status.set_state(ConnectionState::Closing);
        for channel in self.inner.lock().channels.values() {
            channel.set_state(ChannelState::Closing);
        }
    }

    pub(crate) fn set_connection_closed(&self, error: Error) -> Result<()> {
        self.connection_status.set_state(ConnectionState::Closed);
        self.inner
            .lock()
            .channels
            .drain()
            .map(|(id, channel)| {
                self.frames.clear_expected_replies(id, error.clone());
                channel.set_state(ChannelState::Closed);
                channel.error_publisher_confirms(error.clone());
                channel.cancel_consumers()
            })
            .fold(Ok(()), Result::and)
    }

    pub(crate) fn set_connection_error(&self, error: Error) -> Result<()> {
        if let ConnectionState::Error = self.connection_status.state() {
            return Ok(());
        }

        error!("Connection error: {}", error);
        self.connection_status.set_state(ConnectionState::Error);
        self.frames.drop_pending(error.clone());
        self.error_handler.on_error(error.clone());
        self.inner
            .lock()
            .channels
            .drain()
            .map(|(id, channel)| {
                self.frames.clear_expected_replies(id, error.clone());
                channel.set_state(ChannelState::Error);
                channel.error_publisher_confirms(error.clone());
                channel.error_consumers(error.clone())
            })
            .fold(Ok(()), Result::and)
    }

    pub(crate) fn flow(&self) -> bool {
        self.inner
            .lock()
            .channels
            .values()
            .all(|c| c.status().flow())
    }

    pub(crate) fn send_heartbeat(&self) -> Result<()> {
        debug!("send heartbeat");

        self.get(0)
            .map(|channel0| {
                let (promise, resolver) = Promise::new();

                if log_enabled!(Trace) {
                    promise.set_marker("Heartbeat".into());
                }

                channel0.send_frame(AMQPFrame::Heartbeat(0), resolver, None);
                self.internal_rpc.register_internal_future(promise)
            })
            .unwrap_or_else(|| {
                Err(Error::InvalidConnectionState(
                    self.connection_status.state(),
                ))
            })
    }

    pub(crate) fn handle_frame(&self, f: AMQPFrame) -> Result<()> {
        if let Err(err) = self.do_handle_frame(f) {
            self.set_connection_error(err.clone())?;
            Err(err)
        } else {
            Ok(())
        }
    }

    fn do_handle_frame(&self, f: AMQPFrame) -> Result<()> {
        trace!("will handle frame: {:?}", f);
        match f {
            AMQPFrame::ProtocolHeader(version) => {
                error!(
                    "we asked for AMQP {} but the server only supports AMQP {}",
                    ProtocolVersion::amqp_0_9_1(),
                    version
                );
                let error = Error::InvalidProtocolVersion(version);
                if let Some(resolver) = self.connection_status.connection_resolver() {
                    resolver.swear(Err(error.clone()));
                }
                return Err(error);
            }
            AMQPFrame::Method(channel_id, method) => {
                self.receive_method(channel_id, method)?;
            }
            AMQPFrame::Heartbeat(channel_id) => {
                if channel_id == 0 {
                    debug!("received heartbeat from server");
                } else {
                    error!("received invalid heartbeat on channel {}", channel_id);
                    let error = AMQPError::new(
                        AMQPHardError::FRAMEERROR.into(),
                        format!("heartbeat frame received on channel {}", channel_id).into(),
                    );
                    if let Some(Err(error)) = self.get(0).map(|channel0| {
                        self.internal_rpc
                            .register_internal_future(channel0.connection_close(
                                error.get_id(),
                                error.get_message().as_str(),
                                0,
                                0,
                            ))
                    }) {
                        return Err(error);
                    }
                    return Err(Error::ProtocolError(error));
                }
            }
            AMQPFrame::Header(channel_id, class_id, header) => {
                if channel_id == 0 {
                    error!("received content header on channel {}", channel_id);
                    let error = AMQPError::new(
                        AMQPHardError::CHANNELERROR.into(),
                        format!("content header frame received on channel {}", channel_id).into(),
                    );
                    if let Some(Err(error)) = self.get(0).map(|channel0| {
                        self.internal_rpc
                            .register_internal_future(channel0.connection_close(
                                error.get_id(),
                                error.get_message().as_str(),
                                class_id,
                                0,
                            ))
                    }) {
                        return Err(error);
                    }
                    return Err(Error::ProtocolError(error));
                } else {
                    self.handle_content_header_frame(
                        channel_id,
                        class_id,
                        header.body_size,
                        header.properties,
                    )?;
                }
            }
            AMQPFrame::Body(channel_id, payload) => {
                self.handle_body_frame(channel_id, payload)?;
            }
        }
        Ok(())
    }

    pub(crate) fn set_error_handler<E: FnMut(Error) + Send + 'static>(&self, handler: E) {
        self.error_handler.set_handler(handler);
    }

    pub(crate) fn topology(&self) -> Vec<ChannelDefinition> {
        self.inner
            .lock()
            .channels
            .values()
            .filter(|c| c.id() != 0)
            .map(Channel::topology)
            .collect()
    }
}

impl fmt::Debug for Channels {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Channels");
        if let Some(inner) = self.inner.try_lock() {
            debug
                .field("channels", &inner.channels.values())
                .field("channel_id", &inner.channel_id)
                .field("configuration", &inner.configuration);
        }
        debug
            .field("frames", &self.frames)
            .field("executor", &self.executor)
            .field("connection_status", &self.connection_status)
            .field("error_handler", &self.error_handler)
            .finish()
    }
}

struct Inner {
    channels: HashMap<u16, Channel>,
    channel_id: IdSequence<u16>,
    configuration: Configuration,
    waker: SocketStateHandle,
}

impl Inner {
    fn new(configuration: Configuration, waker: SocketStateHandle) -> Self {
        Self {
            channels: HashMap::default(),
            channel_id: IdSequence::new(false),
            configuration,
            waker,
        }
    }

    fn create_channel(
        &mut self,
        id: u16,
        connection_status: ConnectionStatus,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
        connection_closer: Option<Arc<ConnectionCloser>>,
    ) -> Channel {
        debug!("create channel with id {}", id);
        let channel = Channel::new(
            id,
            self.configuration.clone(),
            connection_status,
            self.waker.clone(),
            internal_rpc,
            frames,
            executor,
            connection_closer,
        );
        self.channels.insert(id, channel.clone_internal());
        channel
    }

    fn create(
        &mut self,
        connection_status: ConnectionStatus,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
        connection_closer: Arc<ConnectionCloser>,
    ) -> Result<Channel> {
        debug!("create channel");
        self.channel_id.set_max(self.configuration.channel_max());
        let first_id = self.channel_id.next();
        let mut id = first_id;
        let mut met_first_id = false;
        loop {
            if id == first_id {
                if met_first_id {
                    break;
                }
                met_first_id = true;
            }
            if !self.channels.contains_key(&id) {
                return Ok(self.create_channel(
                    id,
                    connection_status,
                    internal_rpc,
                    frames,
                    executor,
                    Some(connection_closer),
                ));
            }
            id = self.channel_id.next();
        }
        Err(Error::ChannelsLimitReached)
    }
}
<|endoftext|>


<commit_before>// Implements the `zakopane checksum` subcommand.

use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;
type ChecksumResult = Result<String, ZakopaneError>;

#[derive(Default)]
struct ChecksumTaskManager {
    // Non-associative container enumerating paths and checksums.
    sums: std::vec::Vec<(String, String)>,

    // Map of outstanding checksum tasks.
    // *    Keys: absolute paths being checksummed
    // *    Values: tokio JoinHandles for checksum tasks
    tasks: std::collections::HashMap<String, tokio::task::JoinHandle<ChecksumResult>>,
}

async fn do_checksum(path: std::path::PathBuf) -> ChecksumResult {
    todo!()
}

impl ChecksumTaskManager {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn spawn_task(&mut self, path: std::path::PathBuf) {
        assert!(
            self.tasks.len() < MAX_TASKS,
            "attempted to spawn too many tasks"
        );
        self.tasks.insert(String::from(path.to_str().unwrap()), tokio::spawn(do_checksum(path)));
    }
}

async fn checksum_impl() -> ChecksumResult {
    // *    Walk the target path.
    // *    If we can, then spawn a task, providing the current path
    //      in the walk and a cloned transmitter.
    // *    Call `poll_recv()` on the receiver repeatedly, updating
    //      our checksum map and `join()`ing the completed tasks.
    todo!()
}

#[allow(dead_code)]
fn checksum() -> ChecksumResult {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl())
}
<commit_msg>checksum: Introduce channel usage<commit_after>// Implements the `zakopane checksum` subcommand.

use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;
type ChecksumResult = Result<String, ZakopaneError>;

struct ChecksumTaskManager {
    // Non-associative container enumerating paths and checksums.
    sums: std::vec::Vec<(String, String)>,

    // Map of outstanding checksum tasks.
    // *    Keys: absolute paths being checksummed
    // *    Values: tokio JoinHandles for checksum tasks
    tasks: std::collections::HashMap<String, tokio::task::JoinHandle<()>>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
}

async fn do_checksum(path: std::path::PathBuf) {
    todo!()
}

impl ChecksumTaskManager {
    pub fn new() -> Self {
        let (sender, receiver) = tokio::sync::mpsc::channel(MAX_TASKS);
        Self {
            sums: Default::default(),
            tasks: Default::default(),
            sender: sender,
            receiver: receiver,
        }
    }

    pub fn spawn_task(&mut self, path: std::path::PathBuf) {
        assert!(
            self.tasks.len() < MAX_TASKS,
            "attempted to spawn too many tasks"
        );
        self.tasks.insert(
            String::from(path.to_str().unwrap()),
            tokio::spawn(do_checksum(path)),
        );
    }
}

async fn checksum_impl() -> ChecksumResult {
    // *    Walk the target path.
    // *    If we can, then spawn a task, providing the current path
    //      in the walk and a cloned transmitter.
    // *    Call `poll_recv()` on the receiver repeatedly, updating
    //      our checksum map and `join()`ing the completed tasks.
    todo!()
}

#[allow(dead_code)]
fn checksum() -> ChecksumResult {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl())
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.handle_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, mut index: usize) -> usize {
        let goal_depth = self.goal_depth.unwrap();
        while self.current_depth != goal_depth && index < self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            index = index + 1;
        }
        return index;
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            return self.seek_forward(index)
        } else {
            self.current_depth = self.current_depth + 1;
            return index;
        }
    }
}
<commit_msg>Use shorthand return syntax<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.handle_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, mut index: usize) -> usize {
        let goal_depth = self.goal_depth.unwrap();
        while self.current_depth != goal_depth && index < self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            index = index + 1;
        }
        index
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            return self.seek_forward(index)
        } else {
            self.current_depth = self.current_depth + 1;
            return index;
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::fs::DirBuilder;
use std::path::Path;
use super::generation::PageGenerator;

pub fn new_project(parent_dir: &str) -> Result<(), io::Error> {
    try!(DirBuilder::new().recursive(true).create(parent_dir));

    try!(DirBuilder::new().recursive(false).create(format!("{}/pages", parent_dir)));

    Ok(())
}

pub fn build_project() -> Result<(), io::Error> {
    let pages_path = "pages";
    let output_dir = "_site";
    let mut page_generator = PageGenerator::new();

    let directory_iterator = try!(Path::new(pages_path).read_dir());

    match DirBuilder::new().create(format!("{}", output_dir)) {
        Ok(_) => {},
        Err(_) => {},
    };

    for entry in directory_iterator {
        let file = try!(entry);
        let file_type = file.file_type().unwrap();
        let source_file = format!("{}/{}", pages_path, file.file_name().to_str().unwrap());
        let file_stem = String::from(file.path().file_stem().unwrap().to_str().unwrap());
        let destination_file = format!("{}/{}.html", output_dir, file_stem);

        if file_type.is_file() && file.file_name().to_str().unwrap().contains(".md") {
            try!(page_generator.set_input_file(source_file.as_ref())
                                .set_output_file(destination_file.as_ref())
                                .generate());
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use super::*;
    use std::fs::remove_dir_all;
    use std::env;
    use std::path;
    use std::error::Error;
    use std::fs::File;
    use std::io::prelude::*;

    #[test]
    fn it_creates_default_directory_structure() {
        // Setup
        let temp_dir = env::temp_dir();
        let proj_dir = String::from(temp_dir.to_str().unwrap()) + "/test-project0";
        let page_dir = proj_dir.clone() + "/pages";

        new_project(proj_dir.as_ref());

        // Assert directories exists
        assert!(path::Path::new(&proj_dir).exists());
        assert!(path::Path::new(&page_dir).exists());

        // Teardown
        match remove_dir_all(proj_dir) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }

    #[test]
    fn it_parses_md_files_to_html_in_the_project() {
        // Setup
        let temp_dir = env::temp_dir();
        let proj_dir = String::from(temp_dir.to_str().unwrap()) + "/test-project1";
        let page_dir = proj_dir.clone() + "/pages";
        let site_dir = proj_dir.clone() + "/_site";
        let test_file_name = page_dir.clone() + "/test.md";
        let test_file_compiled_name = "_site/test.html";

        new_project(&proj_dir);

        // Add markdown file
        let mut test_file = match File::create(&test_file_name) {
            Ok(file) => file,
            Err(what) => panic!("{}", Error::description(&what))
        };

        test_file.write_all(b"# This is a test");

        env::set_current_dir(&proj_dir);
        build_project();

        let mut test_output_file = match File::open(&test_file_compiled_name) {
            Ok(file) => file,
            Err(what) => panic!("{}", Error::description(&what))
        };

        let mut compiled_contents = String::new();

        test_output_file.read_to_string(&mut compiled_contents);

        let expected = "
This is a test
";

        assert!(path::Path::new(&test_file_compiled_name).exists());
        assert_eq!(expected, compiled_contents.trim());

        // Teardown
        match remove_dir_all(proj_dir) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }
}
<commit_msg>fix: Add wrap option to build command<commit_after>use std::io;
use std::fs::DirBuilder;
use std::path::Path;
use super::generation::PageGenerator;

pub fn new_project(parent_dir: &str) -> Result<(), io::Error> {
    try!(DirBuilder::new().recursive(true).create(parent_dir));

    try!(DirBuilder::new().recursive(false).create(format!("{}/pages", parent_dir)));

    Ok(())
}

pub fn build_project() -> Result<(), io::Error> {
    let pages_path = "pages";
    let output_dir = "_site";
    let mut page_generator = PageGenerator::new();

    let directory_iterator = try!(Path::new(pages_path).read_dir());

    match DirBuilder::new().create(format!("{}", output_dir)) {
        Ok(_) => {},
        Err(_) => {},
    };

    for entry in directory_iterator {
        let file = try!(entry);
        let file_type = file.file_type().unwrap();
        let source_file = format!("{}/{}", pages_path, file.file_name().to_str().unwrap());
        let file_stem = String::from(file.path().file_stem().unwrap().to_str().unwrap());
        let destination_file = format!("{}/{}.html", output_dir, file_stem);

        if file_type.is_file() && file.file_name().to_str().unwrap().contains(".md") {
            try!(page_generator.set_input_file(source_file.as_ref())
                                .set_output_file(destination_file.as_ref())
                                .set_wrap(true)
                                .generate());
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use super::*;
    use std::fs::remove_dir_all;
    use std::env;
    use std::path;
    use std::error::Error;
    use std::fs::File;
    use std::io::prelude::*;

    #[test]
    fn it_creates_default_directory_structure() {
        // Setup
        let temp_dir = env::temp_dir();
        let proj_dir = String::from(temp_dir.to_str().unwrap()) + "/test-project0";
        let page_dir = proj_dir.clone() + "/pages";

        new_project(proj_dir.as_ref());

        // Assert directories exists
        assert!(path::Path::new(&proj_dir).exists());
        assert!(path::Path::new(&page_dir).exists());

        // Teardown
        match remove_dir_all(proj_dir) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }

    #[test]
    fn it_parses_md_files_to_html_in_the_project() {
        // Setup
        let temp_dir = env::temp_dir();
        let proj_dir = String::from(temp_dir.to_str().unwrap()) + "/test-project1";
        let page_dir = proj_dir.clone() + "/pages";
        let site_dir = proj_dir.clone() + "/_site";
        let test_file_name = page_dir.clone() + "/test.md";
        let test_file_compiled_name = "_site/test.html";

        new_project(&proj_dir);

        // Add markdown file
        let mut test_file = match File::create(&test_file_name) {
            Ok(file) => file,
            Err(what) => panic!("{}", Error::description(&what))
        };

        test_file.write_all(b"# This is a test");

        env::set_current_dir(&proj_dir);
        build_project();

        let mut test_output_file = match File::open(&test_file_compiled_name) {
            Ok(file) => file,
            Err(what) => panic!("{}", Error::description(&what))
        };

        let mut compiled_contents = String::new();

        test_output_file.read_to_string(&mut compiled_contents);

        let expected = "<!DOCTYPE html>\n\
                        \n\
                        \n\
                        <title>Aluminum Page</title>\n\
                        \n\
                        \n\

This is a test
\n\
                        \n\
                        ";

        assert!(path::Path::new(&test_file_compiled_name).exists());
        assert_eq!(expected, compiled_contents.trim());

        // Teardown
        match remove_dir_all(proj_dir) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the PWM peripheral.
//!
//! RPPAL configures the Raspberry Pi's PWM peripheral through the `/sys/class/pwm`
//! sysfs interface.
//!
//! ## PWM channels
//!
//! The BCM283x SoC supports two hardware PWM channels. By default, both channels
//! are disabled. To enable only PWM0 on its default pin (BCM GPIO 18, physical pin 12),
//! add `dtoverlay=pwm` to `/boot/config.txt`. If you need both PWM channels, replace
//! `pwm` with `pwm-2chan`, which enables PWM0 on BCM GPIO 18 (physical pin 12), and PWM1
//! on BCM GPIO 19 (physical pin 35). More details on enabling and configuring PWM on
//! other GPIO pins than the default ones can be found in `/boot/overlays/README`.
//!
//! The Raspberry Pi's analog audio output needs both PWM channels. Using both audio out
//! and PWM at the same time may cause issues.
//!
//! Some of the GPIO pins capable of supporting hardware PWM can also be configured for
//! use with other peripherals. Be careful not to enable two peripherals on the same pin
//! at the same time.
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! If [`new`] returns an `io::ErrorKind::PermissionDenied`
//! error, make sure `/sys/class/pwm` and all of its subdirectories
//! are owned by `root:gpio`, the current user is a member of the `gpio` group
//! and udev is properly configured as mentioned above. Alternatively, you can
//! launch your application using `sudo`.
//!
//! ### Not found
//!
//! If [`new`] returns an `io::ErrorKind::NotFound` error, you may have
//! forgotten to enable the selected PWM channel. The configuration options
//! to enable either of the two PWM channels are listed above.
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983
//! [`new`]: struct.Pwm.html#method.new

use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

quick_error! {
/// Errors that can occur when accessing the PWM peripheral.
    #[derive(Debug)]
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `pwm::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// PWM channels.
///
/// More information on enabling and configuring the PWM channels can be
/// found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

/// Output polarities.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

/// Provides access to the Raspberry Pi's PWM peripheral.
///
/// Before using `Pwm`, make sure the selected PWM channel has been configured
/// and activated. More information can be found [here].
///
/// [here]: index.html
pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    /// Constructs a new `Pwm`.
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Default settings
        let _ = pwm.set_duty_cycle(Duration::from_secs(0));
        let _ = pwm.set_period(Duration::from_secs(0));
        let _ = pwm.set_polarity(Polarity::Normal);

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    ///
    /// `duty_cycle` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// This method will fail if `period` is shorter than `duty_cycle`.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_period(
        channel: Channel,
        period: Duration,
        duty_cycle: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(channel as u8, 0);

        pwm.set_period(period)?;
        pwm.set_duty_cycle(duty_cycle)?;
        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `with_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_frequency(
        channel: Channel,
        frequency: f64,
        duty_cycle: f64,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let duty_cycle = period * duty_cycle.min(1.0);

        sysfs::set_period(channel as u8, period as u64)?;
        sysfs::set_duty_cycle(channel as u8, duty_cycle as u64)?;

        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Gets the configured period.
    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    /// Sets the period.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    /// The selected period must be longer than or equal to the duty cycle.
    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Gets the configured duty cycle.
    pub fn duty_cycle(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::duty_cycle(self.channel as u8)?))
    }

    /// Sets the duty cycle.
    ///
    /// `duty_cycle` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period. The
    /// duty cycle must be shorter than or equal to the period.
    ///
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_duty_cycle(&self, duty_cycle: Duration) -> Result<()> {
        sysfs::set_duty_cycle(
            self.channel as u8,
            u64::from(duty_cycle.subsec_nanos())
                .saturating_add(duty_cycle.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Sets the frequency and duty cycle.
    ///
    /// `set_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(self.channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let duty_cycle = period * duty_cycle.min(1.0);

        sysfs::set_period(self.channel as u8, period as u64)?;
        sysfs::set_duty_cycle(self.channel as u8, duty_cycle as u64)?;

        Ok(())
    }

    /// Gets the configured polarity.
    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    /// Sets the polarity.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// By default, `polarity` is set to [`Normal`].
    ///
    /// [`Normal`]: enum.Polarity.html
    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    /// Checks whether PWM is currently enabled on the selected channel.
    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    /// Enables PWM on the selected channel.
    pub fn enable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, true)?;

        Ok(())
    }

    /// Disables PWM on the selected channel.
    pub fn disable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, false)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        let _ = sysfs::set_enabled(self.channel as u8, false);
        let _ = sysfs::unexport(self.channel as u8);
    }
}
<commit_msg>Update documentation<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the PWM peripheral.
//!
//! RPPAL configures the Raspberry Pi's PWM peripheral through the `/sys/class/pwm`
//! sysfs interface.
//!
//! ## PWM channels
//!
//! The BCM283x SoC supports two hardware PWM channels. By default, both channels
//! are disabled. To enable only PWM0 on its default pin (BCM GPIO 18, physical pin 12),
//! add `dtoverlay=pwm` to `/boot/config.txt`. If you need both PWM channels, replace
//! `pwm` with `pwm-2chan`, which enables PWM0 on BCM GPIO 18 (physical pin 12), and PWM1
//! on BCM GPIO 19 (physical pin 35). More details on enabling and configuring PWM on
//! other GPIO pins than the default ones can be found in `/boot/overlays/README`.
//!
//! The Raspberry Pi's analog audio output needs both PWM channels. Using both audio out
//! and PWM at the same time may cause issues.
//!
//! Some of the GPIO pins capable of supporting hardware PWM can also be configured for
//! use with other peripherals. Be careful not to enable two peripherals on the same pin
//! at the same time.
//!
//! ## Using PWM without superuser privileges (`sudo`)
//!
//! As of kernel version 4.14.34, released on April 16 2018, it's possible to
//! configure your Raspberry Pi to allow non-root access to PWM. 4.14.34 includes
//! a [patch] that allows udev to change file permissions when a
//! PWM channel is exported. This will let any user that's a member of the `gpio`
//! group configure PWM without having to use `sudo`.
//!
//! The udev rules needed to make this work haven't been patched in yet as of
//! June 2018, but you can easily add them yourself. Make sure you're running
//! 4.14.34 or later, and append the following snippet to
//! `/etc/udev/rules.d/99-com.rules`. Reboot the Raspberry Pi afterwards.
//!
//! ```text
//! SUBSYSTEM=="pwm*", PROGRAM="/bin/sh -c '\
//!     chown -R root:gpio /sys/class/pwm && chmod -R 770 /sys/class/pwm;\
//!     chown -R root:gpio /sys/devices/platform/soc/*.pwm/pwm/pwmchip* &&\
//!     chmod -R 770 /sys/devices/platform/soc/*.pwm/pwm/pwmchip*\
//! '"
//! ```
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! If [`new`] returns an `io::ErrorKind::PermissionDenied`
//! error, make sure `/sys/class/pwm` and all of its subdirectories
//! are owned by `root:gpio`, the current user is a member of the `gpio` group
//! and udev is properly configured as mentioned above. Alternatively, you can
//! launch your application using `sudo`.
//!
//! ### Not found
//!
//! If [`new`] returns an `io::ErrorKind::NotFound` error, you may have
//! forgotten to enable the selected PWM channel. The configuration options
//! to enable either of the two PWM channels are listed above.
//!
//! [patch]: https://github.com/raspberrypi/linux/issues/1983
//! [`new`]: struct.Pwm.html#method.new

use std::io;
use std::result;
use std::time::Duration;

mod sysfs;

quick_error! {
/// Errors that can occur when accessing the PWM peripheral.
    #[derive(Debug)]
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `pwm::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// PWM channels.
///
/// More information on enabling and configuring the PWM channels can be
/// found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Channel {
    Pwm0 = 0,
    Pwm1 = 1,
}

/// Output polarities.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Polarity {
    Normal,
    Inverse,
}

/// Provides access to the Raspberry Pi's PWM peripheral.
///
/// Before using `Pwm`, make sure the selected PWM channel has been configured
/// and activated. More information can be found [here].
///
/// [here]: index.html
pub struct Pwm {
    channel: Channel,
}

impl Pwm {
    /// Constructs a new `Pwm`.
    pub fn new(channel: Channel) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Default settings
        let _ = pwm.set_duty_cycle(Duration::from_secs(0));
        let _ = pwm.set_period(Duration::from_secs(0));
        let _ = pwm.set_polarity(Polarity::Normal);

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    ///
    /// `duty_cycle` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// This method will fail if `period` is shorter than `duty_cycle`.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_period(
        channel: Channel,
        period: Duration,
        duty_cycle: Duration,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(channel as u8, 0);

        pwm.set_period(period)?;
        pwm.set_duty_cycle(duty_cycle)?;
        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Constructs a new `Pwm` using the specified settings.
    ///
    /// `with_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// `enabled` immediately enables PWM on the selected channel.
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn with_frequency(
        channel: Channel,
        frequency: f64,
        duty_cycle: f64,
        polarity: Polarity,
        enabled: bool,
    ) -> Result<Pwm> {
        sysfs::export(channel as u8)?;

        let pwm = Pwm { channel };

        // Always reset "enable" to 0. The sysfs pwm interface has a bug where a previous
        // export may have left "enable" as 1 after unexporting. On the next export,
        // "enable" is still set to 1, even though the channel isn't enabled.
        let _ = pwm.disable();

        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let duty_cycle = period * duty_cycle.min(1.0);

        sysfs::set_period(channel as u8, period as u64)?;
        sysfs::set_duty_cycle(channel as u8, duty_cycle as u64)?;

        pwm.set_polarity(polarity)?;
        if enabled {
            pwm.enable()?;
        }

        Ok(pwm)
    }

    /// Gets the configured period.
    pub fn period(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::period(self.channel as u8)?))
    }

    /// Sets the period.
    ///
    /// `period` represents the time it takes for the PWM channel to complete one cycle.
    /// The selected period must be longer than or equal to the duty cycle.
    pub fn set_period(&self, period: Duration) -> Result<()> {
        sysfs::set_period(
            self.channel as u8,
            u64::from(period.subsec_nanos())
                .saturating_add(period.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Gets the configured duty cycle.
    pub fn duty_cycle(&self) -> Result<Duration> {
        Ok(Duration::from_nanos(sysfs::duty_cycle(self.channel as u8)?))
    }

    /// Sets the duty cycle.
    ///
    /// `duty_cycle` represents the amount of time the PWM channel's logic level is set
    /// high (or low if the polarity is set to [`Inverse`]) during a single period. The
    /// duty cycle must be shorter than or equal to the period.
    ///
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_duty_cycle(&self, duty_cycle: Duration) -> Result<()> {
        sysfs::set_duty_cycle(
            self.channel as u8,
            u64::from(duty_cycle.subsec_nanos())
                .saturating_add(duty_cycle.as_secs().saturating_mul(1_000_000_000)),
        )?;

        Ok(())
    }

    /// Sets the frequency and duty cycle.
    ///
    /// `set_frequency` is a convenience method that converts `frequency` to a period,
    /// and calculates the duty cycle as a percentage of the frequency.
    ///
    /// `frequency` is specified in herz (Hz).
    ///
    /// `duty_cycle` is specified as a floating point percentage, where `1.0` represents 100%.
    pub fn set_frequency(&self, frequency: f64, duty_cycle: f64) -> Result<()> {
        // Set duty cycle to 0 first in case the new period is shorter than the current duty cycle
        let _ = sysfs::set_duty_cycle(self.channel as u8, 0);

        // Convert to nanoseconds
        let period = (1.0f64 / frequency) * 1_000_000_000f64;
        let duty_cycle = period * duty_cycle.min(1.0);

        sysfs::set_period(self.channel as u8, period as u64)?;
        sysfs::set_duty_cycle(self.channel as u8, duty_cycle as u64)?;

        Ok(())
    }

    /// Gets the configured polarity.
    pub fn polarity(&self) -> Result<Polarity> {
        Ok(sysfs::polarity(self.channel as u8)?)
    }

    /// Sets the polarity.
    ///
    /// `polarity` configures the active logic level as either high ([`Normal`]) or low ([`Inverse`]).
    ///
    /// By default, `polarity` is set to [`Normal`].
    ///
    /// [`Normal`]: enum.Polarity.html
    /// [`Inverse`]: enum.Polarity.html
    pub fn set_polarity(&self, polarity: Polarity) -> Result<()> {
        sysfs::set_polarity(self.channel as u8, polarity)?;

        Ok(())
    }

    /// Checks whether PWM is currently enabled on the selected channel.
    pub fn enabled(&self) -> Result<bool> {
        Ok(sysfs::enabled(self.channel as u8)?)
    }

    /// Enables PWM on the selected channel.
    pub fn enable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, true)?;

        Ok(())
    }

    /// Disables PWM on the selected channel.
    pub fn disable(&self) -> Result<()> {
        sysfs::set_enabled(self.channel as u8, false)?;

        Ok(())
    }
}

impl Drop for Pwm {
    fn drop(&mut self) {
        let _ = sysfs::set_enabled(self.channel as u8, false);
        let _ = sysfs::unexport(self.channel as u8);
    }
}
<|endoftext|>