<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 context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::matching::{self, MatchingContext, MatchingMode};
use smallvec::SmallVec;

/// <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;
        }
    }
}

/// 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,
    quirks_mode: QuirksMode,
) -> Result<(), ()>
where
    E: TElement,
    Q: SelectorQuery<E>,
{
    use selectors::parser::{Component, LocalName};
    use std::borrow::Borrow;

    // 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's just care about the easy cases for now.
    //
    // FIXME(emilio): Blink has a fast path for classes in ancestor combinators
    // that may be worth stealing.
    if selector.len() > 1 {
        return Err(());
    }

    let component = selector.iter().next().unwrap();
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        }
        Component::ID(ref id) => {
            // TODO(emilio): We may want to reuse Gecko's document ID table.
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_id(id, case_sensitivity)
            })
        }
        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(())
}

// 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 fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        quirks_mode,
    );

    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.
    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());

    // 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: Move the single simple selector optimizations to a different function.<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 context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{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;
        }
    }
}

/// 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) => {
            // TODO(emilio): We may want to reuse Gecko's document ID table.
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
                element.has_id(id, case_sensitivity)
            })
        }
        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,
    quirks_mode: QuirksMode,
) -> 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'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,
        );
    }

    // FIXME(emilio): Implement better optimizations for compound selectors and
    // such.
    Err(())
}

// 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 fast_result = query_selector_fast::<E, Q>(
        root,
        selector_list,
        results,
        quirks_mode,
    );

    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.
    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());

    // 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>#![crate_name = "uniq"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Chirag B Jadwani <chirag.jadwani@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

use getopts::{Matches, Options};
use std::cmp::min;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::FromStr;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "uniq";
static VERSION: &'static str = "1.0.0";

struct Uniq {
    repeats_only: bool,
    uniques_only: bool,
    all_repeated: bool,
    delimiters: String,
    show_counts: bool,
    slice_start: Option<usize>,
    slice_stop: Option<usize>,
    ignore_case: bool,
}

impl Uniq {
    pub fn print_uniq<R: Read, W: Write>(&self, reader: &mut BufReader<R>, writer: &mut BufWriter<W>) {
        let mut lines: Vec<String> = vec!();
        let mut first_line_printed = false;
        let delimiters = &self.delimiters[..];

        for io_line in reader.lines() {
            let line = crash_if_err!(1, io_line);
            if !lines.is_empty() && self.cmp_key(&lines[0]) != self.cmp_key(&line) {
                let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed);
                first_line_printed |= self.print_lines(writer, &lines, print_delimiter);
                lines.truncate(0);
            }
            lines.push(line);
        }
        if !lines.is_empty() {
            let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed);
            self.print_lines(writer, &lines, print_delimiter);
        }
    }

    fn cmp_key(&self, line: &String) -> String {
        let len = line.len();
        if len > 0 {
            let slice_start = match self.slice_start {
                Some(i) => min(i, len - 1),
                None => 0
            };
            let slice_stop = match self.slice_stop {
                Some(i) => min(slice_start + i, len),
                None => len
            };

            line[slice_start..slice_stop].chars()
                .map(|c| match c {
                    'a' ... 'z' if self.ignore_case => ((c as u8) - 32) as char,
                    _ => c,
                }).collect()
        } else {
            line.clone()
        }
    }

    fn print_lines<W: Write>(&self, writer: &mut BufWriter<W>, lines: &Vec<String>, print_delimiter: bool) -> bool {
        let mut first_line_printed = false;
        let mut count = if self.all_repeated { 1 } else { lines.len() };
        if lines.len() == 1 && !self.repeats_only
                || lines.len() > 1 && !self.uniques_only {
            self.print_line(writer, &lines[0], count, print_delimiter);
            first_line_printed = true;
            count += 1;
        }
        if self.all_repeated {
            for line in lines[1..].iter() {
                self.print_line(writer, line, count, print_delimiter && !first_line_printed);
                first_line_printed = true;
                count += 1;
            }
        }
        first_line_printed
    }

    fn print_line<W: Write>(&self, writer: &mut BufWriter<W>, line: &String, count: usize, print_delimiter: bool) {
        if print_delimiter {
            crash_if_err!(1, writer.write_all(&['\n' as u8]));
        }

        crash_if_err!(1, if self.show_counts {
            writer.write_all(format!("{:7} {}", count, line).as_bytes())
        } else {
            writer.write_all(line.as_bytes())
        });
        crash_if_err!(1, writer.write_all("\n".as_bytes()));
    }
}

fn opt_parsed<T: FromStr>(opt_name: &str, matches: &Matches) -> Option<T> {
    matches.opt_str(opt_name).map(|arg_str| {
        let opt_val: Option<T> = arg_str.parse().ok();
        opt_val.unwrap_or_else(||
            crash!(1, "Invalid argument for {}: {}", opt_name, arg_str))
    })
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("c", "count", "prefix lines by the number of occurrences");
    opts.optflag("d", "repeated", "only print duplicate lines");
    opts.optflagopt(
        "D",
        "all-repeated",
        "print all duplicate lines delimit-method={none(default),prepend,separate} Delimiting is done with blank lines",
        "delimit-method"
    );
    opts.optopt("s", "skip-chars", "avoid comparing the first N characters", "N");
    opts.optopt("w", "check-chars", "compare no more than N characters in lines", "N");
    opts.optflag("i", "ignore-case", "ignore differences in case when comparing");
    opts.optflag("u", "unique", "only print unique lines");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!("");
        print!("{}", opts.usage("Filter adjacent matching lines from INPUT (or standard input),\n\
                                    writing to OUTPUT (or standard output)."));
        println!("");
        println!("Note: '{0}' does not detect repeated lines unless they are adjacent.\n\
                  You may want to sort the input first, or use 'sort -u' without '{0}'.\n", NAME);
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else {
        let (in_file_name, out_file_name) = match matches.free.len() {
            0 => ("-".to_string(), "-".to_string()),
            1 => (matches.free[0].clone(), "-".to_string()),
            2 => (matches.free[0].clone(), matches.free[1].clone()),
            _ => {
                crash!(1, "Extra operand: {}", matches.free[2]);
            }
        };
        let uniq = Uniq {
            repeats_only: matches.opt_present("repeated") || matches.opt_present("all-repeated"),
            uniques_only: matches.opt_present("unique"),
            all_repeated: matches.opt_present("all-repeated"),
            delimiters: match matches.opt_default("all-repeated", "none") {
                Some(ref opt_arg) if opt_arg != "none" => {
                    let rep_args = ["prepend".to_string(), "separate".to_string()];
                    if !rep_args.contains(opt_arg) {
                        crash!(1, "Incorrect argument for all-repeated: {}", opt_arg.clone());
                    }
                    opt_arg.clone()
                },
                _ => "".to_string()
            },
            show_counts: matches.opt_present("count"),
            slice_start: opt_parsed("skip-chars", &matches),
            slice_stop: opt_parsed("check-chars", &matches),
            ignore_case: matches.opt_present("ignore-case"),
        };
        uniq.print_uniq(&mut open_input_file(in_file_name),
                        &mut open_output_file(out_file_name));
    }
    0
}

fn open_input_file(in_file_name: String) -> BufReader<Box<Read+'static>> {
    let in_file = if in_file_name == "-" {
        Box::new(stdin()) as Box<Read>
    } else {
        let path = Path::new(&in_file_name[..]);
        let in_file = File::open(&path);
        let r = crash_if_err!(1, in_file);
        Box::new(r) as Box<Read>
    };
    BufReader::new(in_file)
}

fn open_output_file(out_file_name: String) -> BufWriter<Box<Write+'static>> {
    let out_file = if out_file_name == "-" {
        Box::new(stdout()) as Box<Write>
    } else {
        let path = Path::new(&out_file_name[..]);
        let in_file = File::create(&path);
        let w = crash_if_err!(1, in_file);
        Box::new(w) as Box<Write>
    };
    BufWriter::new(out_file)
}
<commit_msg>-f flag for uniq<commit_after>#![crate_name = "uniq"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Chirag B Jadwani <chirag.jadwani@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

use getopts::{Matches, Options};
use std::cmp::min;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::FromStr;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "uniq";
static VERSION: &'static str = "1.0.0";

struct Uniq {
    repeats_only: bool,
    uniques_only: bool,
    all_repeated: bool,
    delimiters: String,
    show_counts: bool,
    skip_fields: Option<usize>,
    slice_start: Option<usize>,
    slice_stop: Option<usize>,
    ignore_case: bool,
}

impl Uniq {
    pub fn print_uniq<R: Read, W: Write>(&self, reader: &mut BufReader<R>, writer: &mut BufWriter<W>) {
        let mut lines: Vec<String> = vec!();
        let mut first_line_printed = false;
        let delimiters = &self.delimiters[..];

        for io_line in reader.lines() {
            let line = crash_if_err!(1, io_line);
            if !lines.is_empty() && self.cmp_key(&self.skip_fields(&lines[0])) != self.cmp_key(&self.skip_fields(&line)) {
                let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed);
                first_line_printed |= self.print_lines(writer, &lines, print_delimiter);
                lines.truncate(0);
            }
            lines.push(line);
        }
        if !lines.is_empty() {
            let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed);
            self.print_lines(writer, &lines, print_delimiter);
        }
    }

    fn skip_fields(&self, line: &String) -> String {
        match self.skip_fields {
            Some(skip_fields) =>
                if line.split_whitespace().count() > skip_fields {
                    let mut field = 0;
                    let mut i = 0;
                    while field < skip_fields && i < line.len() {
                        while i < line.len() && line.chars().nth(i).unwrap().is_whitespace() {
                            i = i + 1;
                        }
                        while i < line.len() && !line.chars().nth(i).unwrap().is_whitespace() {
                            i = i + 1;
                        }
                        field = field + 1;
                    }
                    line[i..].to_string()
                } else {
                    "".to_string()
                },
            None => line[..].to_string()
        }
    }

    fn cmp_key(&self, line: &String) -> String {
        let len = line.len();
        if len > 0 {
            let slice_start = match self.slice_start {
                Some(i) => min(i, len - 1),
                None => 0
            };
            let slice_stop = match self.slice_stop {
                Some(i) => min(slice_start + i, len),
                None => len
            };

            line[slice_start..slice_stop].chars()
                .map(|c| match c {
                    'a' ... 'z' if self.ignore_case => ((c as u8) - 32) as char,
                    _ => c,
                }).collect()
        } else {
            line.clone()
        }
    }

    fn print_lines<W: Write>(&self, writer: &mut BufWriter<W>, lines: &Vec<String>, print_delimiter: bool) -> bool {
        let mut first_line_printed = false;
        let mut count = if self.all_repeated { 1 } else { lines.len() };
        if lines.len() == 1 && !self.repeats_only
                || lines.len() > 1 && !self.uniques_only {
            self.print_line(writer, &lines[0], count, print_delimiter);
            first_line_printed = true;
            count += 1;
        }
        if self.all_repeated {
            for line in lines[1..].iter() {
                self.print_line(writer, line, count, print_delimiter && !first_line_printed);
                first_line_printed = true;
                count += 1;
            }
        }
        first_line_printed
    }

    fn print_line<W: Write>(&self, writer: &mut BufWriter<W>, line: &String, count: usize, print_delimiter: bool) {
        if print_delimiter {
            crash_if_err!(1, writer.write_all(&['\n' as u8]));
        }

        crash_if_err!(1, if self.show_counts {
            writer.write_all(format!("{:7} {}", count, line).as_bytes())
        } else {
            writer.write_all(line.as_bytes())
        });
        crash_if_err!(1, writer.write_all("\n".as_bytes()));
    }
}

fn opt_parsed<T: FromStr>(opt_name: &str, matches: &Matches) -> Option<T> {
    matches.opt_str(opt_name).map(|arg_str| {
        let opt_val: Option<T> = arg_str.parse().ok();
        opt_val.unwrap_or_else(||
            crash!(1, "Invalid argument for {}: {}", opt_name, arg_str))
    })
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("c", "count", "prefix lines by the number of occurrences");
    opts.optflag("d", "repeated", "only print duplicate lines");
    opts.optflagopt(
        "D",
        "all-repeated",
        "print all duplicate lines delimit-method={none(default),prepend,separate} Delimiting is done with blank lines",
        "delimit-method"
    );
    opts.optopt("f", "skip-fields", "avoid comparing the first N fields", "N");
    opts.optopt("s", "skip-chars", "avoid comparing the first N characters", "N");
    opts.optopt("w", "check-chars", "compare no more than N characters in lines", "N");
    opts.optflag("i", "ignore-case", "ignore differences in case when comparing");
    opts.optflag("u", "unique", "only print unique lines");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", NAME);
        println!("");
        print!("{}", opts.usage("Filter adjacent matching lines from INPUT (or standard input),\n\
                                    writing to OUTPUT (or standard output)."));
        println!("");
        println!("Note: '{0}' does not detect repeated lines unless they are adjacent.\n\
                  You may want to sort the input first, or use 'sort -u' without '{0}'.\n", NAME);
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else {
        let (in_file_name, out_file_name) = match matches.free.len() {
            0 => ("-".to_string(), "-".to_string()),
            1 => (matches.free[0].clone(), "-".to_string()),
            2 => (matches.free[0].clone(), matches.free[1].clone()),
            _ => {
                crash!(1, "Extra operand: {}", matches.free[2]);
            }
        };
        let uniq = Uniq {
            repeats_only: matches.opt_present("repeated") || matches.opt_present("all-repeated"),
            uniques_only: matches.opt_present("unique"),
            all_repeated: matches.opt_present("all-repeated"),
            delimiters: match matches.opt_default("all-repeated", "none") {
                Some(ref opt_arg) if opt_arg != "none" => {
                    let rep_args = ["prepend".to_string(), "separate".to_string()];
                    if !rep_args.contains(opt_arg) {
                        crash!(1, "Incorrect argument for all-repeated: {}", opt_arg.clone());
                    }
                    opt_arg.clone()
                },
                _ => "".to_string()
            },
            show_counts: matches.opt_present("count"),
            skip_fields: opt_parsed("skip-fields", &matches),
            slice_start: opt_parsed("skip-chars", &matches),
            slice_stop: opt_parsed("check-chars", &matches),
            ignore_case: matches.opt_present("ignore-case"),
        };
        uniq.print_uniq(&mut open_input_file(in_file_name),
                        &mut open_output_file(out_file_name));
    }
    0
}

fn open_input_file(in_file_name: String) -> BufReader<Box<Read+'static>> {
    let in_file = if in_file_name == "-" {
        Box::new(stdin()) as Box<Read>
    } else {
        let path = Path::new(&in_file_name[..]);
        let in_file = File::open(&path);
        let r = crash_if_err!(1, in_file);
        Box::new(r) as Box<Read>
    };
    BufReader::new(in_file)
}

fn open_output_file(out_file_name: String) -> BufWriter<Box<Write+'static>> {
    let out_file = if out_file_name == "-" {
        Box::new(stdout()) as Box<Write>
    } else {
        let path = Path::new(&out_file_name[..]);
        let in_file = File::create(&path);
        let w = crash_if_err!(1, in_file);
        Box::new(w) as Box<Write>
    };
    BufWriter::new(out_file)
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
// If no backend is enabled, a large portion of the codebase is unused.
// So silence this useless warning for the CI.
#![cfg_attr(
    not(any(feature = "winit", feature = "udev")),
    allow(dead_code, unused_imports)
)]

#[macro_use]
extern crate slog;

#[cfg(feature = "udev")]
pub mod cursor;
pub mod drawing;
pub mod input_handler;
pub mod output_map;
pub mod render;
pub mod shell;
pub mod state;
#[cfg(feature = "udev")]
pub mod udev;
pub mod window_map;
#[cfg(feature = "winit")]
pub mod winit;
#[cfg(feature = "xwayland")]
pub mod xwayland;

pub use state::AnvilState;
<commit_msg>Fence the module I guess<commit_after>#![warn(rust_2018_idioms)]
// If no backend is enabled, a large portion of the codebase is unused.
// So silence this useless warning for the CI.
#![cfg_attr(
    not(any(feature = "winit", feature = "udev")),
    allow(dead_code, unused_imports)
)]

#[macro_use]
extern crate slog;

#[cfg(feature = "udev")]
pub mod cursor;
pub mod drawing;
pub mod input_handler;
pub mod output_map;
#[cfg(any(feature = "udev", feature = "winit"))]
pub mod render;
pub mod shell;
pub mod state;
#[cfg(feature = "udev")]
pub mod udev;
pub mod window_map;
#[cfg(feature = "winit")]
pub mod winit;
#[cfg(feature = "xwayland")]
pub mod xwayland;

pub use state::AnvilState;
<|endoftext|>


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

use std::path::Path;

use filesystem;
use users;

use error::{Error, Result};

pub fn set_owner<T: AsRef<Path>, X: AsRef<str>>(path: T, owner: X, group: X) -> Result<()> {
    debug!(
        "Attempting to set owner of {:?} to {:?}",
        &path.as_ref(),
        &owner.as_ref()
    );

    let uid = match users::get_uid_by_name(&owner.as_ref()) {
        Some(user) => user,
        None => {
            let msg = format!(
                "Can't change owner of {:?} to {:?}:{:?}, error getting user.",
                &path.as_ref(),
                &owner.as_ref(),
                &group.as_ref()
            );
            return Err(Error::PermissionFailed(msg));
        }
    };

    let gid = match users::get_gid_by_name(&group.as_ref()) {
        Some(group) => group,
        None => {
            let msg = format!(
                "Can't change owner of {:?} to {:?}:{:?}, error getting group.",
                &path.as_ref(),
                &owner.as_ref(),
                &group.as_ref()
            );
            return Err(Error::PermissionFailed(msg));
        }
    };

    let s_path = match path.as_ref().to_str() {
        Some(s) => s,
        None => {
            return Err(Error::PermissionFailed(
                format!("Invalid path {:?}", &path.as_ref()),
            ))
        }

    };
    let result = filesystem::chown(s_path, uid, gid);

    match result {
        Err(err) => Err(err),
        Ok(0) => Ok(()),
        _ => {
            Err(Error::PermissionFailed(format!(
                "Can't change owner of {:?} to {:?}",
                &path.as_ref(),
                &owner.as_ref()
            )))
        }
    }
}

pub fn set_permissions<T: AsRef<Path>>(path: T, mode: u32) -> Result<()> {
    let s_path = match path.as_ref().to_str() {
        Some(s) => s,
        None => {
            return Err(Error::PermissionFailed(
                format!("Invalid path {:?}", &path.as_ref()),
            ))
        }

    };

    let result = filesystem::chmod(s_path, mode);
    match result {
        Err(err) => Err(err),
        Ok(0) => Ok(()),
        _ => {
            Err(Error::PermissionFailed(format!(
                "Can't set permissions on {:?} to {:?}",
                &path.as_ref(),
                &mode
            )))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;

    use tempdir::TempDir;

    use error::Error;
    use super::*;

    #[test]
    fn chmod_ok_test() {
        let tmp_dir = TempDir::new("foo").expect("create temp dir");
        let file_path = tmp_dir.path().join("test.txt");
        let mut tmp_file = File::create(&file_path).expect("create temp file");
        writeln!(tmp_file, "foobar123").expect("write temp file");

        let mode = 0o745;
        assert!(set_permissions(file_path, mode).is_ok());
        drop(tmp_file);
        tmp_dir.close().expect("delete temp dir");
    }

    #[test]
    fn chmod_fail_test() {
        let mode = 0o745;
        let badpath = Path::new("this_file_should_never_exist_deadbeef");
        if let Err(Error::PermissionFailed(_)) = set_permissions(badpath, mode) {
            assert!(true);
        } else {
            assert!(false);
        }
    }
}
<commit_msg>[core] Show group in debug when calling `util::perm::set_owner()`.<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;

use filesystem;
use users;

use error::{Error, Result};

pub fn set_owner<T: AsRef<Path>, X: AsRef<str>>(path: T, owner: X, group: X) -> Result<()> {
    debug!(
        "Attempting to set owner of {:?} to {:?}:{:?}",
        &path.as_ref(),
        &owner.as_ref(),
        &group.as_ref()
    );

    let uid = match users::get_uid_by_name(&owner.as_ref()) {
        Some(user) => user,
        None => {
            let msg = format!(
                "Can't change owner of {:?} to {:?}:{:?}, error getting user.",
                &path.as_ref(),
                &owner.as_ref(),
                &group.as_ref()
            );
            return Err(Error::PermissionFailed(msg));
        }
    };

    let gid = match users::get_gid_by_name(&group.as_ref()) {
        Some(group) => group,
        None => {
            let msg = format!(
                "Can't change owner of {:?} to {:?}:{:?}, error getting group.",
                &path.as_ref(),
                &owner.as_ref(),
                &group.as_ref()
            );
            return Err(Error::PermissionFailed(msg));
        }
    };

    let s_path = match path.as_ref().to_str() {
        Some(s) => s,
        None => {
            return Err(Error::PermissionFailed(
                format!("Invalid path {:?}", &path.as_ref()),
            ))
        }

    };
    let result = filesystem::chown(s_path, uid, gid);

    match result {
        Err(err) => Err(err),
        Ok(0) => Ok(()),
        _ => {
            Err(Error::PermissionFailed(format!(
                "Can't change owner of {:?} to {:?}:{:?}",
                &path.as_ref(),
                &owner.as_ref(),
                &group.as_ref()
            )))
        }
    }
}

pub fn set_permissions<T: AsRef<Path>>(path: T, mode: u32) -> Result<()> {
    let s_path = match path.as_ref().to_str() {
        Some(s) => s,
        None => {
            return Err(Error::PermissionFailed(
                format!("Invalid path {:?}", &path.as_ref()),
            ))
        }

    };

    let result = filesystem::chmod(s_path, mode);
    match result {
        Err(err) => Err(err),
        Ok(0) => Ok(()),
        _ => {
            Err(Error::PermissionFailed(format!(
                "Can't set permissions on {:?} to {:?}",
                &path.as_ref(),
                &mode
            )))
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;

    use tempdir::TempDir;

    use error::Error;
    use super::*;

    #[test]
    fn chmod_ok_test() {
        let tmp_dir = TempDir::new("foo").expect("create temp dir");
        let file_path = tmp_dir.path().join("test.txt");
        let mut tmp_file = File::create(&file_path).expect("create temp file");
        writeln!(tmp_file, "foobar123").expect("write temp file");

        let mode = 0o745;
        assert!(set_permissions(file_path, mode).is_ok());
        drop(tmp_file);
        tmp_dir.close().expect("delete temp dir");
    }

    #[test]
    fn chmod_fail_test() {
        let mode = 0o745;
        let badpath = Path::new("this_file_should_never_exist_deadbeef");
        if let Err(Error::PermissionFailed(_)) = set_permissions(badpath, mode) {
            assert!(true);
        } else {
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::path::PathBuf;

use failure::{bail, format_err, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use log::{debug, info};
use regex::Regex;

use crate::api::{Ref, Repo};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VcsType {
    Git,
    Unknown,
}

#[derive(Copy, Clone)]
pub enum GitReference<'a> {
    Commit(git2::Oid),
    Symbolic(&'a str),
}

impl<'a> fmt::Display for GitReference<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            GitReference::Commit(ref c) => write!(f, "{}", c),
            GitReference::Symbolic(ref s) => write!(f, "{}", s),
        }
    }
}

#[derive(Debug)]
pub struct CommitSpec {
    pub repo: String,
    pub path: Option<PathBuf>,
    pub rev: String,
    pub prev_rev: Option<String>,
}

impl fmt::Display for CommitSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}@{}", &self.repo, &self.rev)
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VcsProvider {
    Generic,
    Git,
    GitHub,
    GitLab,
    Bitbucket,
    Vsts,
}

impl fmt::Display for VcsProvider {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            VcsProvider::Generic => write!(f, "generic"),
            VcsProvider::Git => write!(f, "git"),
            VcsProvider::GitHub => write!(f, "github"),
            VcsProvider::GitLab => write!(f, "gitlab"),
            VcsProvider::Bitbucket => write!(f, "bitbucket"),
            VcsProvider::Vsts => write!(f, "vsts"),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VcsUrl {
    pub provider: VcsProvider,
    pub id: String,
    pub ty: VcsType,
}

fn parse_rev_range(rng: &str) -> (Option<String>, String) {
    if rng == "" {
        return (None, "HEAD".into());
    }
    let mut iter = rng.rsplitn(2, "..");
    let rev = iter.next().unwrap_or("HEAD");
    (iter.next().map(|x| x.to_string()), rev.to_string())
}

impl CommitSpec {
    pub fn parse(s: &str) -> Result<CommitSpec, Error> {
        lazy_static! {
            static ref SPEC_RE: Regex = Regex::new(r"^([^@#]+)(?:#([^@]+))?(?:@(.+))?$").unwrap();
        }
        if let Some(caps) = SPEC_RE.captures(s) {
            let (prev_rev, rev) = parse_rev_range(caps.get(3).map(|x| x.as_str()).unwrap_or(""));
            Ok(CommitSpec {
                repo: caps[1].to_string(),
                path: caps.get(2).map(|x| PathBuf::from(x.as_str())),
                rev,
                prev_rev,
            })
        } else {
            bail!("Could not parse commit spec '{}'", s)
        }
    }

    pub fn reference(&self) -> GitReference<'_> {
        if let Ok(oid) = git2::Oid::from_str(&self.rev) {
            GitReference::Commit(oid)
        } else {
            GitReference::Symbolic(&self.rev)
        }
    }

    pub fn prev_reference(&self) -> Option<GitReference<'_>> {
        self.prev_rev.as_ref().map(|rev| {
            if let Ok(oid) = git2::Oid::from_str(rev) {
                GitReference::Commit(oid)
            } else {
                GitReference::Symbolic(rev)
            }
        })
    }
}

fn strip_git_suffix(s: &str) -> &str {
    s.trim_end_matches(".git")
}

impl VcsUrl {
    pub fn parse(url: &str) -> VcsUrl {
        lazy_static! {
            static ref GIT_URL_RE: Regex =
                Regex::new(r"^(?:ssh|https?)://(?:[^@]+@)?([^/]+)/(.+)$").unwrap();
            static ref GIT_SSH_RE: Regex = Regex::new(r"^(?:[^@]+@)?([^/]+):(.+)$").unwrap();
        }

        if let Some(caps) = GIT_URL_RE.captures(url) {
            if let Some(rv) = VcsUrl::from_git_parts(&caps[1], &caps[2]) {
                return rv;
            } else {
                return VcsUrl {
                    provider: VcsProvider::Generic,
                    id: format!("{}/{}", &caps[1], strip_git_suffix(&caps[2])),
                    ty: VcsType::Unknown,
                };
            }
        }

        if let Some(caps) = GIT_SSH_RE.captures(url) {
            if let Some(rv) = VcsUrl::from_git_parts(&caps[1], &caps[2]) {
                return rv;
            } else {
                return VcsUrl {
                    provider: VcsProvider::Git,
                    id: url.into(),
                    ty: VcsType::Git,
                };
            }
        }

        VcsUrl {
            provider: VcsProvider::Generic,
            id: url.into(),
            ty: VcsType::Unknown,
        }
    }

    fn from_git_parts(host: &str, path: &str) -> Option<VcsUrl> {
        lazy_static! {
            static ref VS_DOMAIN_RE: Regex = Regex::new(r"^([^.]+)\.visualstudio.com$").unwrap();
            static ref VS_GIT_PATH_RE: Regex = Regex::new(r"^_git/(.+?)(?:\.git)?$").unwrap();
        }
        if host == "github.com" {
            return Some(VcsUrl {
                provider: VcsProvider::GitHub,
                id: strip_git_suffix(path).into(),
                ty: VcsType::Git,
            });
        } else if host == "gitlab.com" {
            return Some(VcsUrl {
                provider: VcsProvider::GitLab,
                id: strip_git_suffix(path).into(),
                ty: VcsType::Git,
            });
        } else if host == "bitbucket.org" {
            return Some(VcsUrl {
                provider: VcsProvider::Bitbucket,
                id: strip_git_suffix(path).into(),
                ty: VcsType::Git,
            });
        } else if let Some(caps) = VS_DOMAIN_RE.captures(host) {
            let username = &caps[1];
            if let Some(caps) = VS_GIT_PATH_RE.captures(path) {
                return Some(VcsUrl {
                    provider: VcsProvider::Vsts,
                    id: format!("{}/{}", username, &caps[1]),
                    ty: VcsType::Git,
                });
            }
        }
        None
    }
}

fn is_matching_url(a: &str, b: &str) -> bool {
    VcsUrl::parse(a) == VcsUrl::parse(b)
}

fn find_reference_url(repo: &str, repos: &[Repo]) -> Result<String, Error> {
    let mut found_non_git = false;
    for configured_repo in repos {
        if configured_repo.name != repo {
            continue;
        }

        match configured_repo.provider.id.as_str() {
            "git"
            | "github"
            | "bitbucket"
            | "visualstudio"
            | "integrations:github"
            | "integrations:github_enterprise"
            | "integrations:gitlab"
            | "integrations:bitbucket"
            | "integrations:vsts" => {
                if let Some(ref url) = configured_repo.url {
                    debug!("  Got reference URL for repo {}: {}", repo, url);
                    return Ok(url.clone());
                }
            }
            _ => {
                debug!("  unknown repository {} skipped", configured_repo);
                found_non_git = true;
            }
        }
    }

    if found_non_git {
        bail!("For non git repositories explicit revisions are required");
    } else {
        bail!("Could not find matching repository for {}", repo);
    }
}

fn find_matching_rev(
    reference: GitReference<'_>,
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<Option<String>, Error> {
    macro_rules! log_match {
        ($ex:expr) => {{
            let val = $ex;
            info!("  -> found matching revision {}", val);
            val
        }};
    }

    info!("Resolving {} ({})", &reference, spec);

    let r = match reference {
        GitReference::Commit(commit) => {
            return Ok(Some(log_match!(commit.to_string())));
        }
        GitReference::Symbolic(r) => r,
    };

    let (repo, discovery) = if let Some(ref path) = spec.path {
        (git2::Repository::open(path)?, false)
    } else {
        (git2::Repository::open_from_env()?, !disable_discovery)
    };

    let reference_url = find_reference_url(&spec.repo, repos)?;
    debug!("  Looking for reference URL {}", &reference_url);

    // direct reference in root repository found.  If we are in discovery
    // mode we want to also check for matching URLs.
    if_chain! {
        if let Ok(remote) = repo.find_remote("origin");
        if let Some(url) = remote.url();
        then {
            if !discovery || is_matching_url(url, &reference_url) {
                debug!("  found match: {} == {}", url, &reference_url);
                let head = repo.revparse_single(r)?;
                return Ok(Some(log_match!(head.id().to_string())));
            } else {
                debug!("  not a match: {} != {}", url, &reference_url);
            }
        }
    }

    // in discovery mode we want to find that repo in associated submodules.
    for submodule in repo.submodules()? {
        if let Some(submodule_url) = submodule.url() {
            debug!("  found submodule with URL {}", submodule_url);
            if is_matching_url(submodule_url, &reference_url) {
                debug!(
                    "  found submodule match: {} == {}",
                    submodule_url, &reference_url
                );
                // heads on submodules is easier so let's start with that
                // because that does not require the submodule to be
                // checked out.
                if r == "HEAD" {
                    if let Some(head_oid) = submodule.head_id() {
                        return Ok(Some(log_match!(head_oid.to_string())));
                    }
                }

                // otherwise we need to open the submodule which requires
                // it to be checked out.
                if let Ok(subrepo) = submodule.open() {
                    let head = subrepo.revparse_single(r)?;
                    return Ok(Some(log_match!(head.id().to_string())));
                }
            } else {
                debug!(
                    "  not a submodule match: {} != {}",
                    submodule_url, &reference_url
                );
            }
        }
    }

    info!("  -> no matching revision found");
    Ok(None)
}

fn find_matching_revs(
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<(Option<String>, String), Error> {
    fn error(r: GitReference<'_>, repo: &str) -> Error {
        format_err!(
            "Could not find commit '{}' for '{}'. If you do not have local \
             checkouts of the repositories in question referencing tags or \
             other references will not work and you need to refer to \
             revisions explicitly.",
            r,
            repo
        )
    }

    let rev = if let Some(rev) =
        find_matching_rev(spec.reference(), &spec, &repos[..], disable_discovery)?
    {
        rev
    } else {
        return Err(error(spec.reference(), &spec.repo));
    };

    let prev_rev = if let Some(rev) = spec.prev_reference() {
        if let Some(rv) = find_matching_rev(rev, &spec, &repos[..], disable_discovery)? {
            Some(rv)
        } else {
            return Err(error(rev, &spec.repo));
        }
    } else {
        None
    };

    Ok((prev_rev, rev))
}

pub fn find_head() -> Result<String, Error> {
    let repo = git2::Repository::open_from_env()?;
    let head = repo.revparse_single("HEAD")?;
    Ok(head.id().to_string())
}

/// Given commit specs and repos this returns a list of head commits
/// from it.
pub fn find_heads(specs: Option<Vec<CommitSpec>>, repos: &[Repo]) -> Result<Vec<Ref>, Error> {
    let mut rv = vec![];

    // if commit specs were explicitly provided find head commits with
    // limited amounts of magic.
    if let Some(specs) = specs {
        for spec in &specs {
            let (prev_rev, rev) = find_matching_revs(&spec, &repos[..], specs.len() == 1)?;
            rv.push(Ref {
                repo: spec.repo.clone(),
                rev,
                prev_rev,
            });
        }

    // otherwise apply all the magic available
    } else {
        for repo in repos {
            let spec = CommitSpec {
                repo: repo.name.to_string(),
                path: None,
                rev: "HEAD".into(),
                prev_rev: None,
            };
            if let Some(rev) = find_matching_rev(spec.reference(), &spec, &repos[..], false)? {
                rv.push(Ref {
                    repo: repo.name.to_string(),
                    rev,
                    prev_rev: None,
                });
            }
        }
    }

    Ok(rv)
}

#[test]
fn test_url_parsing() {
    assert_eq!(
        VcsUrl::parse("http://github.com/mitsuhiko/flask"),
        VcsUrl {
            provider: VcsProvider::GitHub,
            id: "mitsuhiko/flask".into(),
            ty: VcsType::Git,
        }
    );
    assert_eq!(
        VcsUrl::parse("git@github.com:mitsuhiko/flask.git"),
        VcsUrl {
            provider: VcsProvider::GitHub,
            id: "mitsuhiko/flask".into(),
            ty: VcsType::Git,
        }
    );
    assert_eq!(
        VcsUrl::parse("http://bitbucket.org/mitsuhiko/flask"),
        VcsUrl {
            provider: VcsProvider::Bitbucket,
            id: "mitsuhiko/flask".into(),
            ty: VcsType::Git,
        }
    );
    assert_eq!(
        VcsUrl::parse("git@bitbucket.org:mitsuhiko/flask.git"),
        VcsUrl {
            provider: VcsProvider::Bitbucket,
            id: "mitsuhiko/flask".into(),
            ty: VcsType::Git,
        }
    );
    assert_eq!(
        VcsUrl::parse("https://neilmanvar.visualstudio.com/_git/sentry-demo"),
        VcsUrl {
            provider: VcsProvider::Vsts,
            id: "neilmanvar/sentry-demo".into(),
            ty: VcsType::Git,
        }
    );
    assert_eq!(
        VcsUrl::parse("https://github.myenterprise.com/mitsuhiko/flask.git"),
        VcsUrl {
            provider: VcsProvider::Generic,
            id: "github.myenterprise.com/mitsuhiko/flask".into(),
            ty: VcsType::Unknown,
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: VcsProvider::GitLab,
            id: "gitlab-org/gitlab-ce".into(),
            ty: VcsType::Git,
        }
    )
}

#[test]
fn test_url_normalization() {
    assert!(is_matching_url(
        "http://github.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
}
<commit_msg>fix(vcs) Relax VCS url comparisons<commit_after>use std::fmt;
use std::path::PathBuf;

use failure::{bail, format_err, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use log::{debug, info};
use regex::Regex;

use crate::api::{Ref, Repo};

#[derive(Copy, Clone)]
pub enum GitReference<'a> {
    Commit(git2::Oid),
    Symbolic(&'a str),
}

impl<'a> fmt::Display for GitReference<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            GitReference::Commit(ref c) => write!(f, "{}", c),
            GitReference::Symbolic(ref s) => write!(f, "{}", s),
        }
    }
}

#[derive(Debug)]
pub struct CommitSpec {
    pub repo: String,
    pub path: Option<PathBuf>,
    pub rev: String,
    pub prev_rev: Option<String>,
}

impl fmt::Display for CommitSpec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}@{}", &self.repo, &self.rev)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VcsUrl {
    pub provider: String,
    pub id: String,
}

fn parse_rev_range(rng: &str) -> (Option<String>, String) {
    if rng == "" {
        return (None, "HEAD".into());
    }
    let mut iter = rng.rsplitn(2, "..");
    let rev = iter.next().unwrap_or("HEAD");
    (iter.next().map(|x| x.to_string()), rev.to_string())
}

impl CommitSpec {
    pub fn parse(s: &str) -> Result<CommitSpec, Error> {
        lazy_static! {
            static ref SPEC_RE: Regex = Regex::new(r"^([^@#]+)(?:#([^@]+))?(?:@(.+))?$").unwrap();
        }
        if let Some(caps) = SPEC_RE.captures(s) {
            let (prev_rev, rev) = parse_rev_range(caps.get(3).map(|x| x.as_str()).unwrap_or(""));
            Ok(CommitSpec {
                repo: caps[1].to_string(),
                path: caps.get(2).map(|x| PathBuf::from(x.as_str())),
                rev,
                prev_rev,
            })
        } else {
            bail!("Could not parse commit spec '{}'", s)
        }
    }

    pub fn reference(&self) -> GitReference<'_> {
        if let Ok(oid) = git2::Oid::from_str(&self.rev) {
            GitReference::Commit(oid)
        } else {
            GitReference::Symbolic(&self.rev)
        }
    }

    pub fn prev_reference(&self) -> Option<GitReference<'_>> {
        self.prev_rev.as_ref().map(|rev| {
            if let Ok(oid) = git2::Oid::from_str(rev) {
                GitReference::Commit(oid)
            } else {
                GitReference::Symbolic(rev)
            }
        })
    }
}

fn strip_git_suffix(s: &str) -> &str {
    s.trim_end_matches(".git")
}

impl VcsUrl {
    pub fn parse(url: &str) -> VcsUrl {
        lazy_static! {
            static ref GIT_URL_RE: Regex =
                Regex::new(r"^(?:ssh|https?)://(?:[^@]+@)?([^/]+)/(.+)$").unwrap();
            static ref GIT_SSH_RE: Regex = Regex::new(r"^(?:[^@]+@)?([^/]+):(.+)$").unwrap();
        }

        if let Some(caps) = GIT_URL_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        if let Some(caps) = GIT_SSH_RE.captures(url) {
            return VcsUrl::from_git_parts(&caps[1], &caps[2]);
        }

        VcsUrl {
            provider: "".into(),
            id: url.into(),
        }
    }

    fn from_git_parts(host: &str, path: &str) -> VcsUrl {
        lazy_static! {
            static ref VS_DOMAIN_RE: Regex = Regex::new(r"^([^.]+)\.visualstudio.com$").unwrap();
            static ref VS_GIT_PATH_RE: Regex = Regex::new(r"^_git/(.+?)(?:\.git)?$").unwrap();
        }
        if let Some(caps) = VS_DOMAIN_RE.captures(host) {
            let username = &caps[1];
            if let Some(caps) = VS_GIT_PATH_RE.captures(path) {
                return VcsUrl {
                    provider: host.into(),
                    id: format!("{}/{}", username, &caps[1]),
                };
            }
        }
        return VcsUrl {
            provider: host.into(),
            id: strip_git_suffix(path).into(),
        }
    }
}

fn is_matching_url(a: &str, b: &str) -> bool {
    VcsUrl::parse(a) == VcsUrl::parse(b)
}

fn find_reference_url(repo: &str, repos: &[Repo]) -> Result<String, Error> {
    let mut found_non_git = false;
    for configured_repo in repos {
        if configured_repo.name != repo {
            continue;
        }

        match configured_repo.provider.id.as_str() {
            "git"
            | "github"
            | "bitbucket"
            | "visualstudio"
            | "integrations:github"
            | "integrations:github_enterprise"
            | "integrations:gitlab"
            | "integrations:bitbucket"
            | "integrations:vsts" => {
                if let Some(ref url) = configured_repo.url {
                    debug!("  Got reference URL for repo {}: {}", repo, url);
                    return Ok(url.clone());
                }
            }
            _ => {
                debug!("  unknown repository {} skipped", configured_repo);
                found_non_git = true;
            }
        }
    }

    if found_non_git {
        bail!("For non git repositories explicit revisions are required");
    } else {
        bail!("Could not find matching repository for {}", repo);
    }
}

fn find_matching_rev(
    reference: GitReference<'_>,
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<Option<String>, Error> {
    macro_rules! log_match {
        ($ex:expr) => {{
            let val = $ex;
            info!("  -> found matching revision {}", val);
            val
        }};
    }

    info!("Resolving {} ({})", &reference, spec);

    let r = match reference {
        GitReference::Commit(commit) => {
            return Ok(Some(log_match!(commit.to_string())));
        }
        GitReference::Symbolic(r) => r,
    };

    let (repo, discovery) = if let Some(ref path) = spec.path {
        (git2::Repository::open(path)?, false)
    } else {
        (git2::Repository::open_from_env()?, !disable_discovery)
    };

    let reference_url = find_reference_url(&spec.repo, repos)?;
    debug!("  Looking for reference URL {}", &reference_url);

    // direct reference in root repository found.  If we are in discovery
    // mode we want to also check for matching URLs.
    if_chain! {
        if let Ok(remote) = repo.find_remote("origin");
        if let Some(url) = remote.url();
        then {
            if !discovery || is_matching_url(url, &reference_url) {
            debug!("  found match: {} == {}", url, &reference_url);
                let head = repo.revparse_single(r)?;
                return Ok(Some(log_match!(head.id().to_string())));
            } else {
                debug!("  not a match: {} != {}", url, &reference_url);
            }
        }
    }

    // in discovery mode we want to find that repo in associated submodules.
    for submodule in repo.submodules()? {
        if let Some(submodule_url) = submodule.url() {
            debug!("  found submodule with URL {}", submodule_url);
            if is_matching_url(submodule_url, &reference_url) {
                debug!(
                    "  found submodule match: {} == {}",
                    submodule_url, &reference_url
                );
                // heads on submodules is easier so let's start with that
                // because that does not require the submodule to be
                // checked out.
                if r == "HEAD" {
                    if let Some(head_oid) = submodule.head_id() {
                        return Ok(Some(log_match!(head_oid.to_string())));
                    }
                }

                // otherwise we need to open the submodule which requires
                // it to be checked out.
                if let Ok(subrepo) = submodule.open() {
                    let head = subrepo.revparse_single(r)?;
                    return Ok(Some(log_match!(head.id().to_string())));
                }
            } else {
                debug!(
                    "  not a submodule match: {} != {}",
                    submodule_url, &reference_url
                );
            }
        }
    }

    info!("  -> no matching revision found");
    Ok(None)
}

fn find_matching_revs(
    spec: &CommitSpec,
    repos: &[Repo],
    disable_discovery: bool,
) -> Result<(Option<String>, String), Error> {
    fn error(r: GitReference<'_>, repo: &str) -> Error {
        format_err!(
            "Could not find commit '{}' for '{}'. If you do not have local \
             checkouts of the repositories in question referencing tags or \
             other references will not work and you need to refer to \
             revisions explicitly.",
            r,
            repo
        )
    }

    let rev = if let Some(rev) =
        find_matching_rev(spec.reference(), &spec, &repos[..], disable_discovery)?
    {
        rev
    } else {
        return Err(error(spec.reference(), &spec.repo));
    };

    let prev_rev = if let Some(rev) = spec.prev_reference() {
        if let Some(rv) = find_matching_rev(rev, &spec, &repos[..], disable_discovery)? {
            Some(rv)
        } else {
            return Err(error(rev, &spec.repo));
        }
    } else {
        None
    };

    Ok((prev_rev, rev))
}

pub fn find_head() -> Result<String, Error> {
    let repo = git2::Repository::open_from_env()?;
    let head = repo.revparse_single("HEAD")?;
    Ok(head.id().to_string())
}

/// Given commit specs and repos this returns a list of head commits
/// from it.
pub fn find_heads(specs: Option<Vec<CommitSpec>>, repos: &[Repo]) -> Result<Vec<Ref>, Error> {
    let mut rv = vec![];

    // if commit specs were explicitly provided find head commits with
    // limited amounts of magic.
    if let Some(specs) = specs {
        for spec in &specs {
            let (prev_rev, rev) = find_matching_revs(&spec, &repos[..], specs.len() == 1)?;
            rv.push(Ref {
                repo: spec.repo.clone(),
                rev,
                prev_rev,
            });
        }

    // otherwise apply all the magic available
    } else {
        for repo in repos {
            let spec = CommitSpec {
                repo: repo.name.to_string(),
                path: None,
                rev: "HEAD".into(),
                prev_rev: None,
            };
            if let Some(rev) = find_matching_rev(spec.reference(), &spec, &repos[..], false)? {
                rv.push(Ref {
                    repo: repo.name.to_string(),
                    rev,
                    prev_rev: None,
                });
            }
        }
    }

    Ok(rv)
}

#[test]
fn test_url_parsing() {
    assert_eq!(
        VcsUrl::parse("http://github.com/mitsuhiko/flask"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@github.com:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("http://bitbucket.org/mitsuhiko/flask"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@bitbucket.org:mitsuhiko/flask.git"),
        VcsUrl {
            provider: "bitbucket.org".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://neilmanvar.visualstudio.com/_git/sentry-demo"),
        VcsUrl {
            provider: "neilmanvar.visualstudio.com".into(),
            id: "neilmanvar/sentry-demo".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://github.myenterprise.com/mitsuhiko/flask.git"),
        VcsUrl {
            provider: "github.myenterprise.com".into(),
            id: "mitsuhiko/flask".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.example.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.example.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.example.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("https://gitlab.com/gitlab-org/gitlab-ce"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    );
    assert_eq!(
        VcsUrl::parse("git@gitlab.com:gitlab-org/gitlab-ce.git"),
        VcsUrl {
            provider: "gitlab.com".into(),
            id: "gitlab-org/gitlab-ce".into(),
        }
    )
}

#[test]
fn test_url_normalization() {
    assert!(false == is_matching_url(
        "http://github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(false == is_matching_url(
        "git@github.mycompany.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "http://github.com/mitsuhiko/flask",
        "git@github.com:mitsuhiko/flask.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.com/gitlab-org/gitlab-ce",
        "git@gitlab.com:gitlab-org/gitlab-ce.git"
    ));
    assert!(is_matching_url(
        "https://gitlab.example.com/gitlab-org/gitlab-ce",
        "git@gitlab.example.com:gitlab-org/gitlab-ce.git"
    ))
}
<|endoftext|>


<commit_before>pub mod ffi;

use std::result;

use libc::c_char;

const NGX_OK:        ffi::ngx_int_t =  0;
const NGX_ERROR:     ffi::ngx_int_t = -1;
const NGX_AGAIN:     ffi::ngx_int_t = -2;
const NGX_BUSY:      ffi::ngx_int_t = -3;
const NGX_DONE:      ffi::ngx_int_t = -4;
const NGX_DECLINED:  ffi::ngx_int_t = -5;
const NGX_ABORT:     ffi::ngx_int_t = -6;

pub type Result = result::Result<(), ffi::ngx_int_t>;

pub struct Connection {
   raw: *mut ffi::ngx_connection_t,
}

impl Connection {
   pub fn new(raw: *mut ffi::ngx_connection_t) -> Self {
      Connection {
         raw: raw
      }
   }

   pub fn log(&self) -> Option<Log> {
      let raw = unsafe { (*self.raw).log };

      if raw.is_null() {
         None
      } else {
         Some(
            Log::new(raw)
         )
      }
   }
}

pub struct Log {
   pub raw: *mut ffi::ngx_log_t,
}

impl Log {
   pub fn new(raw: *mut ffi::ngx_log_t) -> Self {
      Log {
         raw: raw
      }
   }
}

pub struct HttpRequest {
   raw: *mut ffi::ngx_http_request_t,
}

impl HttpRequest {
   pub fn new(raw: *mut ffi::ngx_http_request_t) -> Self {
      HttpRequest {
         raw: raw
      }
   }

   pub fn connection(&self) -> Option<Connection> {
      let raw = unsafe { (*self.raw).connection };

      if raw.is_null() {
         None
      } else {
         Some(
            Connection::new(raw)
         )
      }
   }

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = &mut unsafe { (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Result {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };

      if rc == NGX_OK {
         Ok(())
      } else {
         Err(rc)
      }
   }
}

pub struct HttpHeadersOut {
   raw: *mut ffi::ngx_http_headers_out_t,
}

impl HttpHeadersOut {
   pub fn new(raw: *mut ffi::ngx_http_headers_out_t) -> Self {
      HttpHeadersOut {
         raw: raw
      }
   }

   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}
<commit_msg>Placing the unsafe block in the right place.<commit_after>pub mod ffi;

use std::result;

use libc::c_char;

const NGX_OK:        ffi::ngx_int_t =  0;
const NGX_ERROR:     ffi::ngx_int_t = -1;
const NGX_AGAIN:     ffi::ngx_int_t = -2;
const NGX_BUSY:      ffi::ngx_int_t = -3;
const NGX_DONE:      ffi::ngx_int_t = -4;
const NGX_DECLINED:  ffi::ngx_int_t = -5;
const NGX_ABORT:     ffi::ngx_int_t = -6;

pub type Result = result::Result<(), ffi::ngx_int_t>;

pub struct Connection {
   raw: *mut ffi::ngx_connection_t,
}

impl Connection {
   pub fn new(raw: *mut ffi::ngx_connection_t) -> Self {
      Connection {
         raw: raw
      }
   }

   pub fn log(&self) -> Option<Log> {
      let raw = unsafe { (*self.raw).log };

      if raw.is_null() {
         None
      } else {
         Some(
            Log::new(raw)
         )
      }
   }
}

pub struct Log {
   pub raw: *mut ffi::ngx_log_t,
}

impl Log {
   pub fn new(raw: *mut ffi::ngx_log_t) -> Self {
      Log {
         raw: raw
      }
   }
}

pub struct HttpRequest {
   raw: *mut ffi::ngx_http_request_t,
}

impl HttpRequest {
   pub fn new(raw: *mut ffi::ngx_http_request_t) -> Self {
      HttpRequest {
         raw: raw
      }
   }

   pub fn connection(&self) -> Option<Connection> {
      let raw = unsafe { (*self.raw).connection };

      if raw.is_null() {
         None
      } else {
         Some(
            Connection::new(raw)
         )
      }
   }

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = unsafe { &mut (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Result {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };

      if rc == NGX_OK {
         Ok(())
      } else {
         Err(rc)
      }
   }
}

pub struct HttpHeadersOut {
   raw: *mut ffi::ngx_http_headers_out_t,
}

impl HttpHeadersOut {
   pub fn new(raw: *mut ffi::ngx_http_headers_out_t) -> Self {
      HttpHeadersOut {
         raw: raw
      }
   }

   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}
<|endoftext|>


<commit_before>#![crate_name = "nice"]
#![feature(collections, core, old_io, os, rustc_private, std_misc)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::ffi::CString;
use std::old_io::IoError;
use std::os;
use libc::{c_char, c_int, execvp};

const NAME: &'static str = "nice";
const VERSION: &'static str = "1.0.0";

// XXX: PRIO_PROCESS is 0 on at least FreeBSD and Linux.  Don't know about Mac OS X.
const PRIO_PROCESS: c_int = 0;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

extern {
    fn getpriority(which: c_int, who: c_int) -> c_int;
    fn setpriority(which: c_int, who: c_int, prio: c_int) -> c_int;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let opts = [
        getopts::optopt("n", "adjustment", "add N to the niceness (default is 10)", "N"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(err) => {
            show_error!("{}", err);
            return 125;
        }
    };

    if matches.opt_present("version") || matches.opt_present("help") {
        println!("{} v{}", NAME, VERSION);
        if matches.opt_present("help") {
            let usage = getopts::usage("Run COMMAND with an adjusted niceness, \
                                        which affects process scheduling.\n\
                                        With no COMMAND, print the current \
                                        niceness.  Niceness values range from \
                                        at\nleast -20 (most favorable to the \
                                        process) to 19 (least favorable to the\
                                        \nprocess).", &opts);
            println!("");
            println!("Usage:");
            println!("  {} [OPTIONS] [COMMAND [ARGS]]", NAME);
            println!("");
            print!("{}", usage);
        }
        0
    } else {
        let mut niceness = unsafe { getpriority(PRIO_PROCESS, 0) };
        if os::errno() != 0 {
            show_error!("{}", IoError::last_error());
            return 125;
        }

        let adjustment = match matches.opt_str("adjustment") {
            Some(nstr) => {
                if matches.free.len() == 0 {
                    show_error!("A command must be given with an adjustment.
                                 Try \"{} --help\" for more information.", args[0]);
                    return 125;
                }
                match nstr.as_slice().parse() {
                    Ok(num) => num,
                    Err(e)=> {
                        show_error!("\"{}\" is not a valid number: {}", nstr, e);
                        return 125;
                    }
                }
            },
            None => {
                if matches.free.len() == 0 {
                    println!("{}", niceness);
                    return 0;
                }
                10 as c_int
            }
        };

        niceness += adjustment;
        unsafe { setpriority(PRIO_PROCESS, 0, niceness); }
        if os::errno() != 0 {
            show_warning!("{}", IoError::last_error());
        }

        let cstrs : Vec<CString> = matches.free.iter().map(|x| CString::new(x.as_bytes()).unwrap()).collect();
        let mut args : Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect();
        args.push(0 as *const c_char);
        unsafe { execvp(args[0], args.as_mut_ptr()); }

        show_error!("{}", IoError::last_error());
        if os::errno() as c_int == libc::ENOENT { 127 } else { 126 }
    }
}
<commit_msg>Fix nice.<commit_after>#![crate_name = "nice"]
#![feature(rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::ffi::CString;
use std::io::{Error, Write};
use libc::{c_char, c_int, execvp};

const NAME: &'static str = "nice";
const VERSION: &'static str = "1.0.0";

// XXX: PRIO_PROCESS is 0 on at least FreeBSD and Linux.  Don't know about Mac OS X.
const PRIO_PROCESS: c_int = 0;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

extern {
    fn getpriority(which: c_int, who: c_int) -> c_int;
    fn setpriority(which: c_int, who: c_int, prio: c_int) -> c_int;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let opts = [
        getopts::optopt("n", "adjustment", "add N to the niceness (default is 10)", "N"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(err) => {
            show_error!("{}", err);
            return 125;
        }
    };

    if matches.opt_present("version") || matches.opt_present("help") {
        println!("{} v{}", NAME, VERSION);
        if matches.opt_present("help") {
            let usage = getopts::usage("Run COMMAND with an adjusted niceness, \
                                        which affects process scheduling.\n\
                                        With no COMMAND, print the current \
                                        niceness.  Niceness values range from \
                                        at\nleast -20 (most favorable to the \
                                        process) to 19 (least favorable to the\
                                        \nprocess).", &opts);
            println!("");
            println!("Usage:");
            println!("  {} [OPTIONS] [COMMAND [ARGS]]", NAME);
            println!("");
            print!("{}", usage);
        }
        0
    } else {
        let mut niceness = unsafe { getpriority(PRIO_PROCESS, 0) };
        if Error::last_os_error().raw_os_error().unwrap() != 0 {
            show_error!("{}", Error::last_os_error());
            return 125;
        }

        let adjustment = match matches.opt_str("adjustment") {
            Some(nstr) => {
                if matches.free.len() == 0 {
                    show_error!("A command must be given with an adjustment.
                                 Try \"{} --help\" for more information.", args[0]);
                    return 125;
                }
                match nstr.parse() {
                    Ok(num) => num,
                    Err(e)=> {
                        show_error!("\"{}\" is not a valid number: {}", nstr, e);
                        return 125;
                    }
                }
            },
            None => {
                if matches.free.len() == 0 {
                    println!("{}", niceness);
                    return 0;
                }
                10 as c_int
            }
        };

        niceness += adjustment;
        unsafe { setpriority(PRIO_PROCESS, 0, niceness); }
        if Error::last_os_error().raw_os_error().unwrap() != 0 {
            show_warning!("{}", Error::last_os_error());
        }

        let cstrs: Vec<CString> = matches.free.iter().map(|x| CString::new(x.as_bytes()).unwrap()).collect();
        let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect();
        args.push(0 as *const c_char);
        unsafe { execvp(args[0], args.as_mut_ptr()); }

        show_error!("{}", Error::last_os_error());
        if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT { 127 } else { 126 }
    }
}
<|endoftext|>


<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// A value that can be sent or received through this API.
/// FIXME: Define.
pub struct Value; // FIXME: Define

/// The public API.
///
/// This API is subdivided in traits purely for the sake of
/// namespacing.
pub trait API {
    /// The subset of the API dedicated to nodes.
    type NodeAPI: NodeAPI;
    fn get_node_api(&self) -> Self::NodeAPI;

    /// The subset of the API dedicated to services.
    type ServiceAPI: ServiceAPI;
    fn get_service_api(&self) -> Self::ServiceAPI;
}

/// Node-level API
pub trait NodeAPI {
    /// Get a list of nodes matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/node/list`
    fn get_list(&self, &NodeRequest) -> Vec<Node>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/node/tag/$NodeId`
    fn put_tag(&self, &NodeId, String) -> Result<(), Error>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/node/tag/$NodeId`
    fn delete_tag(&self, &NodeId, String) -> Result<(), Error>;
}

/// Service-level API
pub trait ServiceAPI {
    /// A value that causes a disconnection once it is dropped.
    type Guard;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/service/list`
    fn get_input_services(&self, &InputRequest) -> Vec<Service<Input>>;
    fn get_output_services(&self, &OutputRequest) -> Vec<Service<Output>>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate service.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/tag/$ServiceId`
    fn put_tag(&self, &ServiceId, String) -> Result<(), Error>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate services.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/service/tag/$ServiceId`
    fn delete_tag(&self, &ServiceId, String) -> Result<(), Error>;

    /// Read one value from an input enpoint
    ///
    /// # REST API
    ///
    /// GET /api/v1/service/value/$ServiceId
    fn get_service_value(&self, &Service<Input>) -> Result<Value, Error>;

    /// Send one value to an output enpoint
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/value/$ServiceId`
    fn put_service_value(&self, &Service<Output>, Value) -> Result<(), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/service/watch/$ServiceId`
    fn register_watch<F>(&self, &Service<Input>, &WatchOptions, cb: F)
                         -> Result<Self::Guard, Error>
        where F: Fn(Value) + Send;
}

/// Options for watching a service.
/// FIXME: Define.
pub struct WatchOptions;
<commit_msg>api now uses value<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// The public API.
///
/// This API is subdivided in traits purely for the sake of
/// namespacing.
pub trait API {
    /// The subset of the API dedicated to nodes.
    type NodeAPI: NodeAPI;
    fn get_node_api(&self) -> Self::NodeAPI;

    /// The subset of the API dedicated to services.
    type ServiceAPI: ServiceAPI;
    fn get_service_api(&self) -> Self::ServiceAPI;
}

/// Node-level API
pub trait NodeAPI {
    /// Get a list of nodes matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/node/list`
    fn get_list(&self, &NodeRequest) -> Vec<Node>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/node/tag/$NodeId`
    fn put_tag(&self, &NodeId, String) -> Result<(), Error>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/node/tag/$NodeId`
    fn delete_tag(&self, &NodeId, String) -> Result<(), Error>;
}

/// Service-level API
pub trait ServiceAPI {
    /// A value that causes a disconnection once it is dropped.
    type Guard;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/service/list`
    fn get_input_services(&self, &InputRequest) -> Vec<Service<Input>>;
    fn get_output_services(&self, &OutputRequest) -> Vec<Service<Output>>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate service.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/tag/$ServiceId`
    fn put_tag(&self, &ServiceId, String) -> Result<(), Error>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate services.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/service/tag/$ServiceId`
    fn delete_tag(&self, &ServiceId, String) -> Result<(), Error>;

    /// Read one value from an input enpoint
    ///
    /// # REST API
    ///
    /// GET /api/v1/service/value/$ServiceId
    fn get_service_value(&self, &Service<Input>) -> Result<Value, Error>;

    /// Send one value to an output enpoint
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/value/$ServiceId`
    fn put_service_value(&self, &Service<Output>, Value) -> Result<(), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/service/watch/$ServiceId`
    fn register_watch<F>(&self, &Service<Input>, &WatchOptions, cb: F)
                         -> Result<Self::Guard, Error>
        where F: Fn(Value) + Send;
}

/// Options for watching a service.
/// FIXME: Define.
pub struct WatchOptions;
<|endoftext|>