<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]

use graphql_syntax::GraphQLSource;
use std::iter::Peekable;
use std::str::CharIndices;

/// A wrapper around a peekable char iterator that tracks
/// the column and line indicies.
pub struct CharReader<'a> {
    chars: Peekable<CharIndices<'a>>,
    line_index: usize,
    column_index: usize,
}

impl<'a> CharReader<'a> {
    pub fn new(input: &'a str) -> Self {
        let chars = input.char_indices().peekable();
        CharReader {
            chars,
            line_index: 0,
            column_index: 0,
        }
    }

    // Manually implement `peek` since `Peekable` would call next()
    // and increment the line/column indicies
    fn peek(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }
}

impl<'a> Iterator for CharReader<'a> {
    type Item = (usize, char);
    fn next(&mut self) -> Option<Self::Item> {
        let pair = self.chars.next();
        if let Some((_index, ch)) = pair {
            match ch {
                // Line terminators: https://www.ecma-international.org/ecma-262/#sec-line-terminators
                '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => {
                    // <CRLF>
                    if ch == '\u{000D}' {
                        if let Some((_, ch)) = self.chars.peek() {
                            if ch == &'\u{00A}' {
                                return pair;
                            }
                        }
                    }
                    self.line_index += 1;
                    self.column_index = 0;
                }
                _ => {
                    self.column_index += 1;
                }
            }
        }
        pair
    }
}

/// Extract graphql`text` literals from JS-like code. This should work for Flow
/// or TypeScript alike.
pub fn parse_chunks(input: &str) -> Vec<GraphQLSource> {
    if !input.contains("graphql`") {
        return vec![];
    }
    let mut res = vec![];
    let mut it = CharReader::new(input);
    'code: while let Some((i, c)) = it.next() {
        match c {
            'g' => {
                for expected in ['r', 'a', 'p', 'h', 'q', 'l', '`'].iter() {
                    if let Some((_, c)) = it.next() {
                        if c != *expected {
                            consume_identifier(&mut it);
                            continue 'code;
                        }
                    }
                }
                let start = i;
                let line_index = it.line_index;
                let column_index = it.column_index;
                let mut has_visited_first_char = false;
                while let Some((i, c)) = it.next() {
                    match c {
                        '`' => {
                            let end = i;
                            let text = &input[start + 8..end];
                            res.push(GraphQLSource::new(text, line_index, column_index));
                            continue 'code;
                        }
                        ' ' | '\n' | '\r' => {}
                        'a'..='z' | 'A'..='Z' | '#' => {
                            if !has_visited_first_char {
                                has_visited_first_char = true;
                            }
                        }
                        _ => {
                            if !has_visited_first_char {
                                continue 'code;
                            }
                        }
                    }
                }
            }
            'a'..='z' | 'A'..='Z' | '_' => {
                consume_identifier(&mut it);
            }
            // Skip over template literals. Unfortunately, this isn't enough to
            // deal with nested template literals and runs a risk of skipping
            // over too much code.
            //   '`' => {
            //       while let Some((_, c)) = it.next() {
            //           match c {
            //               '`' => {
            //                   continue 'code;
            //               }
            //               '\\' => {
            //                   it.next();
            //               }
            //               _ => {}
            //           }
            //       }
            //   }
            '"' => consume_string(&mut it, '"'),
            '\'' => consume_string(&mut it, '\''),
            '/' => match it.next() {
                Some((_, '/')) => {
                    consume_line_comment(&mut it);
                }
                Some((_, '*')) => {
                    consume_block_comment(&mut it);
                }
                _ => {}
            },
            _ => {}
        };
    }
    res
}

fn consume_identifier(it: &mut CharReader<'_>) {
    for (_, c) in it {
        match c {
            'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => {}
            _ => {
                return;
            }
        }
    }
}

fn consume_line_comment(it: &mut CharReader<'_>) {
    for (_, c) in it {
        match c {
            '\n' | '\r' => {
                break;
            }
            _ => {}
        }
    }
}

fn consume_block_comment(it: &mut CharReader<'_>) {
    let mut first = true;
    while let Some((_, c)) = it.next() {
        if first && c == '`' {
            break;
        }
        first = false;
        if c == '*' {
            if let Some((_, '/')) = it.peek() {
                it.next();
                break;
            }
        }
    }
}

fn consume_string(it: &mut CharReader<'_>, quote: char) {
    while let Some((_, c)) = it.next() {
        match c {
            '\\' => {
                it.next();
            }
            '\'' | '"' if c == quote => {
                return;
            }
            '\n' | '\r' => {
                // Unexpected newline, terminate the string parsing to recover
                return;
            }
            _ => {}
        }
    }
}
<commit_msg>Leverage IntoIterator for arrays<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]

use graphql_syntax::GraphQLSource;
use std::iter::Peekable;
use std::str::CharIndices;

/// A wrapper around a peekable char iterator that tracks
/// the column and line indicies.
pub struct CharReader<'a> {
    chars: Peekable<CharIndices<'a>>,
    line_index: usize,
    column_index: usize,
}

impl<'a> CharReader<'a> {
    pub fn new(input: &'a str) -> Self {
        let chars = input.char_indices().peekable();
        CharReader {
            chars,
            line_index: 0,
            column_index: 0,
        }
    }

    // Manually implement `peek` since `Peekable` would call next()
    // and increment the line/column indicies
    fn peek(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }
}

impl<'a> Iterator for CharReader<'a> {
    type Item = (usize, char);
    fn next(&mut self) -> Option<Self::Item> {
        let pair = self.chars.next();
        if let Some((_index, ch)) = pair {
            match ch {
                // Line terminators: https://www.ecma-international.org/ecma-262/#sec-line-terminators
                '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => {
                    // <CRLF>
                    if ch == '\u{000D}' {
                        if let Some((_, ch)) = self.chars.peek() {
                            if ch == &'\u{00A}' {
                                return pair;
                            }
                        }
                    }
                    self.line_index += 1;
                    self.column_index = 0;
                }
                _ => {
                    self.column_index += 1;
                }
            }
        }
        pair
    }
}

/// Extract graphql`text` literals from JS-like code. This should work for Flow
/// or TypeScript alike.
pub fn parse_chunks(input: &str) -> Vec<GraphQLSource> {
    if !input.contains("graphql`") {
        return vec![];
    }
    let mut res = vec![];
    let mut it = CharReader::new(input);
    'code: while let Some((i, c)) = it.next() {
        match c {
            'g' => {
                for expected in ['r', 'a', 'p', 'h', 'q', 'l', '`'] {
                    if let Some((_, c)) = it.next() {
                        if c != expected {
                            consume_identifier(&mut it);
                            continue 'code;
                        }
                    }
                }
                let start = i;
                let line_index = it.line_index;
                let column_index = it.column_index;
                let mut has_visited_first_char = false;
                while let Some((i, c)) = it.next() {
                    match c {
                        '`' => {
                            let end = i;
                            let text = &input[start + 8..end];
                            res.push(GraphQLSource::new(text, line_index, column_index));
                            continue 'code;
                        }
                        ' ' | '\n' | '\r' => {}
                        'a'..='z' | 'A'..='Z' | '#' => {
                            if !has_visited_first_char {
                                has_visited_first_char = true;
                            }
                        }
                        _ => {
                            if !has_visited_first_char {
                                continue 'code;
                            }
                        }
                    }
                }
            }
            'a'..='z' | 'A'..='Z' | '_' => {
                consume_identifier(&mut it);
            }
            // Skip over template literals. Unfortunately, this isn't enough to
            // deal with nested template literals and runs a risk of skipping
            // over too much code.
            //   '`' => {
            //       while let Some((_, c)) = it.next() {
            //           match c {
            //               '`' => {
            //                   continue 'code;
            //               }
            //               '\\' => {
            //                   it.next();
            //               }
            //               _ => {}
            //           }
            //       }
            //   }
            '"' => consume_string(&mut it, '"'),
            '\'' => consume_string(&mut it, '\''),
            '/' => match it.next() {
                Some((_, '/')) => {
                    consume_line_comment(&mut it);
                }
                Some((_, '*')) => {
                    consume_block_comment(&mut it);
                }
                _ => {}
            },
            _ => {}
        };
    }
    res
}

fn consume_identifier(it: &mut CharReader<'_>) {
    for (_, c) in it {
        match c {
            'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => {}
            _ => {
                return;
            }
        }
    }
}

fn consume_line_comment(it: &mut CharReader<'_>) {
    for (_, c) in it {
        match c {
            '\n' | '\r' => {
                break;
            }
            _ => {}
        }
    }
}

fn consume_block_comment(it: &mut CharReader<'_>) {
    let mut first = true;
    while let Some((_, c)) = it.next() {
        if first && c == '`' {
            break;
        }
        first = false;
        if c == '*' {
            if let Some((_, '/')) = it.peek() {
                it.next();
                break;
            }
        }
    }
}

fn consume_string(it: &mut CharReader<'_>, quote: char) {
    while let Some((_, c)) = it.next() {
        match c {
            '\\' => {
                it.next();
            }
            '\'' | '"' if c == quote => {
                return;
            }
            '\n' | '\r' => {
                // Unexpected newline, terminate the string parsing to recover
                return;
            }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::ptr;
use std::str::{self, FromStr};

use libc;
use url::Url;

use glib::translate::*;
use glib_sys::{gboolean, gpointer, GList};

use crate::allowed_url::AllowedUrl;
use crate::attributes::Attribute;
use crate::croco::*;
use crate::error::LoadingError;
use crate::io::{self, BinaryData};
use crate::properties::{
    parse_attribute_value_into_parsed_property, ParsedProperty, SpecifiedValues,
};
use crate::util::utf8_cstr;

struct Declaration {
    property: ParsedProperty,
    important: bool,
}

// Maps property_name -> Declaration
type DeclarationList = HashMap<Attribute, Declaration>;

type Selector = String;

/// Contains all the mappings of selectors to style declarations
/// that result from loading an SVG document.
pub struct CssRules {
    selectors_to_declarations: HashMap<Selector, DeclarationList>,
}

impl CssRules {
    pub fn new() -> CssRules {
        CssRules {
            selectors_to_declarations: HashMap::new(),
        }
    }

    pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
        if buf.len() == 0 {
            return; // libcroco doesn't like empty strings :(
        }

        unsafe {
            let mut handler_data = DocHandlerData {
                base_url,
                css_rules: self,
                selector: ptr::null_mut(),
            };

            let doc_handler = cr_doc_handler_new();
            init_cr_doc_handler(&mut *doc_handler);

            (*doc_handler).app_data = &mut handler_data as *mut _ as gpointer;

            let buf_ptr = buf.as_ptr() as *mut _;
            let buf_len = buf.len() as libc::c_ulong;

            let parser = cr_parser_new_from_buf(buf_ptr, buf_len, CR_UTF_8, false.to_glib());
            if parser.is_null() {
                cr_doc_handler_unref(doc_handler);
                return;
            }

            cr_parser_set_sac_handler(parser, doc_handler);
            cr_doc_handler_unref(doc_handler);

            cr_parser_set_use_core_grammar(parser, false.to_glib());
            cr_parser_parse(parser);

            cr_parser_destroy(parser);
        }
    }

    pub fn load_css(&mut self, aurl: &AllowedUrl) -> Result<(), LoadingError> {
        io::acquire_data(aurl, None)
            .and_then(|data| {
                let BinaryData {
                    data: bytes,
                    content_type,
                } = data;

                if content_type.as_ref().map(String::as_ref) == Some("text/css") {
                    Ok(bytes)
                } else {
                    rsvg_log!("\"{}\" is not of type text/css; ignoring", aurl);
                    Err(LoadingError::BadCss)
                }
            })
            .and_then(|bytes| {
                String::from_utf8(bytes).map_err(|_| {
                    rsvg_log!(
                        "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                        aurl
                    );
                    LoadingError::BadCss
                })
            })
            .and_then(|utf8| {
                self.parse(Some(&aurl), &utf8);
                Ok(()) // FIXME: return CSS parsing errors
            })
    }

    fn define(
        &mut self,
        selector: &str,
        attr: Attribute,
        property: ParsedProperty,
        important: bool,
    ) {
        let decl_list = self
            .selectors_to_declarations
            .entry(selector.to_string())
            .or_insert_with(|| DeclarationList::new());

        match decl_list.entry(attr) {
            Entry::Occupied(mut e) => {
                let decl = e.get_mut();

                if !decl.important {
                    decl.property = property;
                    decl.important = important;
                }
            }

            Entry::Vacant(v) => {
                v.insert(Declaration {
                    property,
                    important,
                });
            }
        }
    }

    /// Takes CSS rules which match the given `selector` name and applies them
    /// to the `values`.
    pub fn lookup_apply(
        &self,
        selector: &str,
        values: &mut SpecifiedValues,
        important_styles: &mut HashSet<Attribute>,
    ) -> bool {
        if let Some(decl_list) = self.selectors_to_declarations.get(selector) {
            for (attr, declaration) in decl_list.iter() {
                values.set_style_pair_from_parsed_property(
                    *attr,
                    &declaration.property,
                    declaration.important,
                    important_styles,
                );
            }

            true
        } else {
            false
        }
    }
}

struct DocHandlerData<'a> {
    base_url: Option<&'a Url>,
    css_rules: &'a mut CssRules,
    selector: *mut CRSelector,
}

macro_rules! get_doc_handler_data {
    ($doc_handler:expr) => {
        &mut *((*$doc_handler).app_data as *mut DocHandlerData)
    };
}

fn init_cr_doc_handler(handler: &mut CRDocHandler) {
    handler.import_style = Some(css_import_style);
    handler.start_selector = Some(css_start_selector);
    handler.end_selector = Some(css_end_selector);
    handler.property = Some(css_property);
    handler.error = Some(css_error);
    handler.unrecoverable_error = Some(css_unrecoverable_error);
}

unsafe extern "C" fn css_import_style(
    a_this: *mut CRDocHandler,
    _a_media_list: *mut GList,
    a_uri: CRString,
    _a_uri_default_ns: CRString,
    _a_location: CRParsingLocation,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_uri.is_null() {
        return;
    }

    let raw_uri = cr_string_peek_raw_str(a_uri);
    let uri = utf8_cstr(raw_uri);

    if let Ok(aurl) = AllowedUrl::from_href(uri, handler_data.base_url) {
        // FIXME: handle CSS errors
        let _ = handler_data.css_rules.load_css(&aurl);
    } else {
        rsvg_log!("disallowed URL \"{}\" for importing CSS", uri);
    }
}

unsafe extern "C" fn css_start_selector(
    a_this: *mut CRDocHandler,
    a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_ref(a_selector_list);
    handler_data.selector = a_selector_list;
}

unsafe extern "C" fn css_end_selector(
    a_this: *mut CRDocHandler,
    _a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_unref(handler_data.selector);
    handler_data.selector = ptr::null_mut();
}

unsafe extern "C" fn css_property(
    a_this: *mut CRDocHandler,
    a_name: CRString,
    a_expression: CRTerm,
    a_is_important: gboolean,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_name.is_null() || a_expression.is_null() || handler_data.selector.is_null() {
        return;
    }

    let mut cur_sel = handler_data.selector;
    while !cur_sel.is_null() {
        let simple_sel = (*cur_sel).simple_sel;

        if !simple_sel.is_null() {
            let raw_selector_name = cr_simple_sel_to_string(simple_sel) as *mut libc::c_char;

            if !raw_selector_name.is_null() {
                let raw_prop_name = cr_string_peek_raw_str(a_name);
                let prop_name = utf8_cstr(raw_prop_name);

                let prop_value =
                    <String as FromGlibPtrFull<_>>::from_glib_full(cr_term_to_string(a_expression));

                let selector_name =
                    <String as FromGlibPtrFull<_>>::from_glib_full(raw_selector_name);

                let important = from_glib(a_is_important);

                if let Ok(attr) = Attribute::from_str(prop_name) {
                    if let Ok(Some(prop)) =
                        parse_attribute_value_into_parsed_property(attr, &prop_value, true)
                    {
                        handler_data
                            .css_rules
                            .define(&selector_name, attr, prop, important);
                    }
                }
            }
        }

        cur_sel = (*cur_sel).next;
    }
}

unsafe extern "C" fn css_error(_a_this: *mut CRDocHandler) {
    println!("CSS parsing error");
}

unsafe extern "C" fn css_unrecoverable_error(_a_this: *mut CRDocHandler) {
    println!("CSS unrecoverable error");
}
<commit_msg>Store the corresponding attribute in the Declaration<commit_after>use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::ptr;
use std::str::{self, FromStr};

use libc;
use url::Url;

use glib::translate::*;
use glib_sys::{gboolean, gpointer, GList};

use crate::allowed_url::AllowedUrl;
use crate::attributes::Attribute;
use crate::croco::*;
use crate::error::LoadingError;
use crate::io::{self, BinaryData};
use crate::properties::{
    parse_attribute_value_into_parsed_property, ParsedProperty, SpecifiedValues,
};
use crate::util::utf8_cstr;

struct Declaration {
    attribute: Attribute,
    property: ParsedProperty,
    important: bool,
}

// Maps property_name -> Declaration
type DeclarationList = HashMap<Attribute, Declaration>;

type Selector = String;

/// Contains all the mappings of selectors to style declarations
/// that result from loading an SVG document.
pub struct CssRules {
    selectors_to_declarations: HashMap<Selector, DeclarationList>,
}

impl CssRules {
    pub fn new() -> CssRules {
        CssRules {
            selectors_to_declarations: HashMap::new(),
        }
    }

    pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
        if buf.len() == 0 {
            return; // libcroco doesn't like empty strings :(
        }

        unsafe {
            let mut handler_data = DocHandlerData {
                base_url,
                css_rules: self,
                selector: ptr::null_mut(),
            };

            let doc_handler = cr_doc_handler_new();
            init_cr_doc_handler(&mut *doc_handler);

            (*doc_handler).app_data = &mut handler_data as *mut _ as gpointer;

            let buf_ptr = buf.as_ptr() as *mut _;
            let buf_len = buf.len() as libc::c_ulong;

            let parser = cr_parser_new_from_buf(buf_ptr, buf_len, CR_UTF_8, false.to_glib());
            if parser.is_null() {
                cr_doc_handler_unref(doc_handler);
                return;
            }

            cr_parser_set_sac_handler(parser, doc_handler);
            cr_doc_handler_unref(doc_handler);

            cr_parser_set_use_core_grammar(parser, false.to_glib());
            cr_parser_parse(parser);

            cr_parser_destroy(parser);
        }
    }

    pub fn load_css(&mut self, aurl: &AllowedUrl) -> Result<(), LoadingError> {
        io::acquire_data(aurl, None)
            .and_then(|data| {
                let BinaryData {
                    data: bytes,
                    content_type,
                } = data;

                if content_type.as_ref().map(String::as_ref) == Some("text/css") {
                    Ok(bytes)
                } else {
                    rsvg_log!("\"{}\" is not of type text/css; ignoring", aurl);
                    Err(LoadingError::BadCss)
                }
            })
            .and_then(|bytes| {
                String::from_utf8(bytes).map_err(|_| {
                    rsvg_log!(
                        "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                        aurl
                    );
                    LoadingError::BadCss
                })
            })
            .and_then(|utf8| {
                self.parse(Some(&aurl), &utf8);
                Ok(()) // FIXME: return CSS parsing errors
            })
    }

    fn define(
        &mut self,
        selector: &str,
        attr: Attribute,
        property: ParsedProperty,
        important: bool,
    ) {
        let decl_list = self
            .selectors_to_declarations
            .entry(selector.to_string())
            .or_insert_with(|| DeclarationList::new());

        match decl_list.entry(attr) {
            Entry::Occupied(mut e) => {
                let decl = e.get_mut();

                if !decl.important {
                    decl.property = property;
                    decl.important = important;
                }
            }

            Entry::Vacant(v) => {
                v.insert(Declaration {
                    attribute: attr,
                    property,
                    important,
                });
            }
        }
    }

    /// Takes CSS rules which match the given `selector` name and applies them
    /// to the `values`.
    pub fn lookup_apply(
        &self,
        selector: &str,
        values: &mut SpecifiedValues,
        important_styles: &mut HashSet<Attribute>,
    ) -> bool {
        if let Some(decl_list) = self.selectors_to_declarations.get(selector) {
            for (_, declaration) in decl_list.iter() {
                values.set_style_pair_from_parsed_property(
                    declaration.attribute,
                    &declaration.property,
                    declaration.important,
                    important_styles,
                );
            }

            true
        } else {
            false
        }
    }
}

struct DocHandlerData<'a> {
    base_url: Option<&'a Url>,
    css_rules: &'a mut CssRules,
    selector: *mut CRSelector,
}

macro_rules! get_doc_handler_data {
    ($doc_handler:expr) => {
        &mut *((*$doc_handler).app_data as *mut DocHandlerData)
    };
}

fn init_cr_doc_handler(handler: &mut CRDocHandler) {
    handler.import_style = Some(css_import_style);
    handler.start_selector = Some(css_start_selector);
    handler.end_selector = Some(css_end_selector);
    handler.property = Some(css_property);
    handler.error = Some(css_error);
    handler.unrecoverable_error = Some(css_unrecoverable_error);
}

unsafe extern "C" fn css_import_style(
    a_this: *mut CRDocHandler,
    _a_media_list: *mut GList,
    a_uri: CRString,
    _a_uri_default_ns: CRString,
    _a_location: CRParsingLocation,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_uri.is_null() {
        return;
    }

    let raw_uri = cr_string_peek_raw_str(a_uri);
    let uri = utf8_cstr(raw_uri);

    if let Ok(aurl) = AllowedUrl::from_href(uri, handler_data.base_url) {
        // FIXME: handle CSS errors
        let _ = handler_data.css_rules.load_css(&aurl);
    } else {
        rsvg_log!("disallowed URL \"{}\" for importing CSS", uri);
    }
}

unsafe extern "C" fn css_start_selector(
    a_this: *mut CRDocHandler,
    a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_ref(a_selector_list);
    handler_data.selector = a_selector_list;
}

unsafe extern "C" fn css_end_selector(
    a_this: *mut CRDocHandler,
    _a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_unref(handler_data.selector);
    handler_data.selector = ptr::null_mut();
}

unsafe extern "C" fn css_property(
    a_this: *mut CRDocHandler,
    a_name: CRString,
    a_expression: CRTerm,
    a_is_important: gboolean,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_name.is_null() || a_expression.is_null() || handler_data.selector.is_null() {
        return;
    }

    let mut cur_sel = handler_data.selector;
    while !cur_sel.is_null() {
        let simple_sel = (*cur_sel).simple_sel;

        if !simple_sel.is_null() {
            let raw_selector_name = cr_simple_sel_to_string(simple_sel) as *mut libc::c_char;

            if !raw_selector_name.is_null() {
                let raw_prop_name = cr_string_peek_raw_str(a_name);
                let prop_name = utf8_cstr(raw_prop_name);

                let prop_value =
                    <String as FromGlibPtrFull<_>>::from_glib_full(cr_term_to_string(a_expression));

                let selector_name =
                    <String as FromGlibPtrFull<_>>::from_glib_full(raw_selector_name);

                let important = from_glib(a_is_important);

                if let Ok(attr) = Attribute::from_str(prop_name) {
                    if let Ok(Some(prop)) =
                        parse_attribute_value_into_parsed_property(attr, &prop_value, true)
                    {
                        handler_data
                            .css_rules
                            .define(&selector_name, attr, prop, important);
                    }
                }
            }
        }

        cur_sel = (*cur_sel).next;
    }
}

unsafe extern "C" fn css_error(_a_this: *mut CRDocHandler) {
    println!("CSS parsing error");
}

unsafe extern "C" fn css_unrecoverable_error(_a_this: *mut CRDocHandler) {
    println!("CSS unrecoverable error");
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::sync::Arc;

use protobuf::RepeatedField;
use futures::Future;
use grpc::EnvBuilder;

use kvproto::metapb;
use kvproto::pdpb::{self, Member};
use kvproto::pdpb_grpc;

use super::super::PdFuture;
use super::super::{Result, Error, PdClient, RegionStat};
use super::util::{validate_endpoints, sync_request, check_resp_header, LeaderClient};

const CQ_COUNT: usize = 1;
const CLIENT_PREFIX: &'static str = "pd";

pub struct RpcClient {
    cluster_id: u64,
    leader_client: LeaderClient,
}

impl RpcClient {
    pub fn new(endpoints: &str) -> Result<RpcClient> {
        let endpoints: Vec<_> = endpoints.split(',')
            .map(|s| if !s.starts_with("http://") {
                format!("http://{}", s)
            } else {
                s.to_owned()
            })
            .collect();

        let env = Arc::new(EnvBuilder::new().cq_count(CQ_COUNT).name_prefix(CLIENT_PREFIX).build());
        let (client, members) = try!(validate_endpoints(env.clone(), &endpoints));

        Ok(RpcClient {
            cluster_id: members.get_header().get_cluster_id(),
            leader_client: LeaderClient::new(env, client, members),
        })
    }

    fn header(&self) -> pdpb::RequestHeader {
        let mut header = pdpb::RequestHeader::new();
        header.set_cluster_id(self.cluster_id);
        header
    }

    // For tests
    pub fn get_leader(&self) -> Member {
        self.leader_client.get_leader()
    }
}

impl fmt::Debug for RpcClient {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "PD gRPC Client connects to cluster {:?}",
               self.cluster_id)
    }
}

const LEADER_CHANGE_RETRY: usize = 10;

impl PdClient for RpcClient {
    fn get_cluster_id(&self) -> Result<u64> {
        Ok(self.cluster_id)
    }

    fn bootstrap_cluster(&self, stores: metapb::Store, region: metapb::Region) -> Result<()> {
        let mut req = pdpb::BootstrapRequest::new();
        req.set_header(self.header());
        req.set_store(stores);
        req.set_region(region);

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.bootstrap(req.clone())));
        try!(check_resp_header(resp.get_header()));
        Ok(())
    }

    fn is_cluster_bootstrapped(&self) -> Result<bool> {
        let mut req = pdpb::IsBootstrappedRequest::new();
        req.set_header(self.header());

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.is_bootstrapped(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.get_bootstrapped())
    }

    fn alloc_id(&self) -> Result<u64> {
        let mut req = pdpb::AllocIDRequest::new();
        req.set_header(self.header());

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.alloc_id(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.get_id())
    }

    fn put_store(&self, store: metapb::Store) -> Result<()> {
        let mut req = pdpb::PutStoreRequest::new();
        req.set_header(self.header());
        req.set_store(store);

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.put_store(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(())
    }

    fn get_store(&self, store_id: u64) -> Result<metapb::Store> {
        let mut req = pdpb::GetStoreRequest::new();
        req.set_header(self.header());
        req.set_store_id(store_id);

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_store(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_store())
    }

    fn get_cluster_config(&self) -> Result<metapb::Cluster> {
        let mut req = pdpb::GetClusterConfigRequest::new();
        req.set_header(self.header());

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_cluster_config(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_cluster())
    }

    fn get_region(&self, key: &[u8]) -> Result<metapb::Region> {
        let mut req = pdpb::GetRegionRequest::new();
        req.set_header(self.header());
        req.set_region_key(key.to_vec());

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_region(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_region())
    }

    fn get_region_by_id(&self, region_id: u64) -> PdFuture<Option<metapb::Region>> {
        let mut req = pdpb::GetRegionByIDRequest::new();
        req.set_header(self.header());
        req.set_region_id(region_id);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::GetRegionByIDRequest| {
            let handler = client.get_region_by_id_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|mut resp| {
                    try!(check_resp_header(resp.get_header()));
                    if resp.has_region() {
                        Ok(Some(resp.take_region()))
                    } else {
                        Ok(None)
                    }
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn region_heartbeat(&self,
                        region: metapb::Region,
                        leader: metapb::Peer,
                        region_stat: RegionStat)
                        -> PdFuture<pdpb::RegionHeartbeatResponse> {
        let mut req = pdpb::RegionHeartbeatRequest::new();
        req.set_header(self.header());
        req.set_region(region);
        req.set_leader(leader);
        req.set_down_peers(RepeatedField::from_vec(region_stat.down_peers));
        req.set_pending_peers(RepeatedField::from_vec(region_stat.pending_peers));
        req.set_bytes_written(region_stat.written_bytes);
        req.set_keys_written(region_stat.written_keys);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::RegionHeartbeatRequest| {
            let handler = client.region_heartbeat_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(resp)
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn ask_split(&self, region: metapb::Region) -> PdFuture<pdpb::AskSplitResponse> {
        let mut req = pdpb::AskSplitRequest::new();
        req.set_header(self.header());
        req.set_region(region);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::AskSplitRequest| {
            let handler = client.ask_split_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(resp)
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn store_heartbeat(&self, stats: pdpb::StoreStats) -> PdFuture<()> {
        let mut req = pdpb::StoreHeartbeatRequest::new();
        req.set_header(self.header());
        req.set_stats(stats);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::StoreHeartbeatRequest| {
            let handler = client.store_heartbeat_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(())
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn report_split(&self, left: metapb::Region, right: metapb::Region) -> PdFuture<()> {
        let mut req = pdpb::ReportSplitRequest::new();
        req.set_header(self.header());
        req.set_left(left);
        req.set_right(right);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::ReportSplitRequest| {
            let handler = client.report_split_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(())
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }
}
<commit_msg>pd: better Debug format (#1926)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::sync::Arc;

use protobuf::RepeatedField;
use futures::Future;
use grpc::EnvBuilder;

use kvproto::metapb;
use kvproto::pdpb::{self, Member};
use kvproto::pdpb_grpc;

use super::super::PdFuture;
use super::super::{Result, Error, PdClient, RegionStat};
use super::util::{validate_endpoints, sync_request, check_resp_header, LeaderClient};

const CQ_COUNT: usize = 1;
const CLIENT_PREFIX: &'static str = "pd";

pub struct RpcClient {
    cluster_id: u64,
    leader_client: LeaderClient,
}

impl RpcClient {
    pub fn new(endpoints: &str) -> Result<RpcClient> {
        let endpoints: Vec<_> = endpoints.split(',')
            .map(|s| if !s.starts_with("http://") {
                format!("http://{}", s)
            } else {
                s.to_owned()
            })
            .collect();

        let env = Arc::new(EnvBuilder::new().cq_count(CQ_COUNT).name_prefix(CLIENT_PREFIX).build());
        let (client, members) = try!(validate_endpoints(env.clone(), &endpoints));

        Ok(RpcClient {
            cluster_id: members.get_header().get_cluster_id(),
            leader_client: LeaderClient::new(env, client, members),
        })
    }

    fn header(&self) -> pdpb::RequestHeader {
        let mut header = pdpb::RequestHeader::new();
        header.set_cluster_id(self.cluster_id);
        header
    }

    pub fn get_leader(&self) -> Member {
        self.leader_client.get_leader()
    }
}

impl fmt::Debug for RpcClient {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("RpcClient")
            .field("cluster_id", &self.cluster_id)
            .field("leader", &self.get_leader())
            .finish()
    }
}

const LEADER_CHANGE_RETRY: usize = 10;

impl PdClient for RpcClient {
    fn get_cluster_id(&self) -> Result<u64> {
        Ok(self.cluster_id)
    }

    fn bootstrap_cluster(&self, stores: metapb::Store, region: metapb::Region) -> Result<()> {
        let mut req = pdpb::BootstrapRequest::new();
        req.set_header(self.header());
        req.set_store(stores);
        req.set_region(region);

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.bootstrap(req.clone())));
        try!(check_resp_header(resp.get_header()));
        Ok(())
    }

    fn is_cluster_bootstrapped(&self) -> Result<bool> {
        let mut req = pdpb::IsBootstrappedRequest::new();
        req.set_header(self.header());

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.is_bootstrapped(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.get_bootstrapped())
    }

    fn alloc_id(&self) -> Result<u64> {
        let mut req = pdpb::AllocIDRequest::new();
        req.set_header(self.header());

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.alloc_id(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.get_id())
    }

    fn put_store(&self, store: metapb::Store) -> Result<()> {
        let mut req = pdpb::PutStoreRequest::new();
        req.set_header(self.header());
        req.set_store(store);

        let resp = try!(sync_request(&self.leader_client,
                                     LEADER_CHANGE_RETRY,
                                     |client| client.put_store(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(())
    }

    fn get_store(&self, store_id: u64) -> Result<metapb::Store> {
        let mut req = pdpb::GetStoreRequest::new();
        req.set_header(self.header());
        req.set_store_id(store_id);

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_store(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_store())
    }

    fn get_cluster_config(&self) -> Result<metapb::Cluster> {
        let mut req = pdpb::GetClusterConfigRequest::new();
        req.set_header(self.header());

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_cluster_config(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_cluster())
    }

    fn get_region(&self, key: &[u8]) -> Result<metapb::Region> {
        let mut req = pdpb::GetRegionRequest::new();
        req.set_header(self.header());
        req.set_region_key(key.to_vec());

        let mut resp = try!(sync_request(&self.leader_client,
                                         LEADER_CHANGE_RETRY,
                                         |client| client.get_region(req.clone())));
        try!(check_resp_header(resp.get_header()));

        Ok(resp.take_region())
    }

    fn get_region_by_id(&self, region_id: u64) -> PdFuture<Option<metapb::Region>> {
        let mut req = pdpb::GetRegionByIDRequest::new();
        req.set_header(self.header());
        req.set_region_id(region_id);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::GetRegionByIDRequest| {
            let handler = client.get_region_by_id_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|mut resp| {
                    try!(check_resp_header(resp.get_header()));
                    if resp.has_region() {
                        Ok(Some(resp.take_region()))
                    } else {
                        Ok(None)
                    }
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn region_heartbeat(&self,
                        region: metapb::Region,
                        leader: metapb::Peer,
                        region_stat: RegionStat)
                        -> PdFuture<pdpb::RegionHeartbeatResponse> {
        let mut req = pdpb::RegionHeartbeatRequest::new();
        req.set_header(self.header());
        req.set_region(region);
        req.set_leader(leader);
        req.set_down_peers(RepeatedField::from_vec(region_stat.down_peers));
        req.set_pending_peers(RepeatedField::from_vec(region_stat.pending_peers));
        req.set_bytes_written(region_stat.written_bytes);
        req.set_keys_written(region_stat.written_keys);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::RegionHeartbeatRequest| {
            let handler = client.region_heartbeat_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(resp)
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn ask_split(&self, region: metapb::Region) -> PdFuture<pdpb::AskSplitResponse> {
        let mut req = pdpb::AskSplitRequest::new();
        req.set_header(self.header());
        req.set_region(region);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::AskSplitRequest| {
            let handler = client.ask_split_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(resp)
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn store_heartbeat(&self, stats: pdpb::StoreStats) -> PdFuture<()> {
        let mut req = pdpb::StoreHeartbeatRequest::new();
        req.set_header(self.header());
        req.set_stats(stats);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::StoreHeartbeatRequest| {
            let handler = client.store_heartbeat_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(())
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }

    fn report_split(&self, left: metapb::Region, right: metapb::Region) -> PdFuture<()> {
        let mut req = pdpb::ReportSplitRequest::new();
        req.set_header(self.header());
        req.set_left(left);
        req.set_right(right);

        let executor = |client: &pdpb_grpc::PdClient, req: pdpb::ReportSplitRequest| {
            let handler = client.report_split_async(req);
            handler.map_err(Error::Grpc)
                .and_then(|resp| {
                    try!(check_resp_header(resp.get_header()));
                    Ok(())
                })
                .boxed()
        };

        self.leader_client
            .request(req, executor, LEADER_CHANGE_RETRY)
            .execute()
    }
}
<|endoftext|>


<commit_before>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::PathBuf};

use crate::{common::ui::UI,
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::Result,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";

const STUDIO_CMD: &str = "hab-studio";
const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

#[derive(Clone, Copy)]
enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(CTL_SECRET_ENVVAR,
                            config.ctl_secret,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";

    pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init();
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    let command = exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = super::STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    command
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "linux")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use std::{ffi::OsString,
              path::PathBuf,
              str::FromStr};

    pub fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split('/').collect();
                let ident = PackageIdent::from_str(&format!("{}/{}",
                                                            super::STUDIO_PACKAGE_IDENT,
                                                            version[0]))?;
                exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        true
    }
}
<commit_msg>Who needs semicolons<commit_after>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::PathBuf};

use crate::{common::ui::UI,
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::Result,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";

const STUDIO_CMD: &str = "hab-studio";
const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

#[derive(Clone, Copy)]
enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(CTL_SECRET_ENVVAR,
                            config.ctl_secret,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";

    pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init();
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    let command = exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?;
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = super::STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    command
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "linux")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use std::{ffi::OsString,
              path::PathBuf,
              str::FromStr};

    pub fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split('/').collect();
                let ident = PackageIdent::from_str(&format!("{}/{}",
                                                            super::STUDIO_PACKAGE_IDENT,
                                                            version[0]))?;
                exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        true
    }
}
<|endoftext|>


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

use dom::activation::Activatable;
use dom::attr::AttrValue;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLAnchorElementDerived, HTMLImageElementDerived};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::{MouseEventCast, NodeCast};
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::document::{Document, DocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::element::{Element, AttributeHandlers, ElementTypeId};
use dom::event::Event;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::WindowHelpers;

use num::ToPrimitive;
use std::default::Default;
use string_cache::Atom;
use util::str::DOMString;

use url::UrlParser;

#[dom_struct]
pub struct HTMLAnchorElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableHeap<JS<DOMTokenList>>,
}

impl HTMLAnchorElementDerived for EventTarget {
    fn is_htmlanchorelement(&self) -> bool {
        *self.type_id() ==
            EventTargetTypeId::Node(
                NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)))
    }
}

impl HTMLAnchorElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLAnchorElement {
        HTMLAnchorElement {
            htmlelement:
                HTMLElement::new_inherited(HTMLElementTypeId::HTMLAnchorElement, localName, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLAnchorElement> {
        let element = HTMLAnchorElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLAnchorElementBinding::Wrap)
    }
}

impl<'a> VirtualMethods for &'a HTMLAnchorElement {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &&HTMLElement = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("rel") => AttrValue::from_serialized_tokenlist(value),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl<'a> HTMLAnchorElementMethods for &'a HTMLAnchorElement {
    // https://html.spec.whatwg.org/multipage/#dom-a-text
    fn Text(self) -> DOMString {
        let node = NodeCast::from_ref(self);
        node.GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/#dom-a-text
    fn SetText(self, value: DOMString) {
        let node = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }

    // https://html.spec.whatwg.org/multipage/#dom-a-rellist
    fn RelList(self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(ElementCast::from_ref(self), &atom!("rel"))
        })
    }
}

impl<'a> Activatable for &'a HTMLAnchorElement {
    fn as_element<'b>(&'b self) -> &'b Element {
        ElementCast::from_ref(*self)
    }

    fn is_instance_activatable(&self) -> bool {
        // https://html.spec.whatwg.org/multipage/#hyperlink
        // "a [...] element[s] with an href attribute [...] must [..] create a
        // hyperlink"
        // https://html.spec.whatwg.org/multipage/#the-a-element
        // "The activation behaviour of a elements *that create hyperlinks*"
        ElementCast::from_ref(*self).has_attribute(&atom!("href"))
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    fn pre_click_activation(&self) {
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
    fn canceled_activation(&self) {
    }

    //https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour
    fn activation_behavior(&self, event: &Event, target: &EventTarget) {
        //Step 1. If the node document is not fully active, abort.
        let doc = document_from_node(*self);
        if !doc.r().is_fully_active() {
            return;
        }
        //TODO: Step 2. Check if browsing context is specified and act accordingly.
        //Step 3. Handle <img ismap/>.
        let element = ElementCast::from_ref(*self);
        let mouse_event = MouseEventCast::to_ref(event).unwrap();
        let mut ismap_suffix = None;
        if let Some(element) = ElementCast::to_ref(target) {
            if target.is_htmlimageelement() && element.has_attribute(&atom!("ismap")) {

                let target_node = NodeCast::from_ref(element);
                let rect = window_from_node(target_node).r().content_box_query(
                    target_node.to_trusted_node_address());
                ismap_suffix = Some(
                    format!("?{},{}", mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
                                      mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px())
                )
            }
        }

        //TODO: Step 4. Download the link is `download` attribute is set.

        let href = element.get_attribute(&ns!(""), &atom!("href")).unwrap();
        let mut value = href.r().Value();
        if let Some(suffix) = ismap_suffix {
            value.push_str(&suffix);
        }
        debug!("clicked on link to {}", value);

        let window = doc.window();
        let base_url = window.get_url();
        let url = UrlParser::new().base_url(&base_url).parse(&value);
        // FIXME: handle URL parse errors more gracefully.
        let url = url.unwrap();
        window.load_url(url);
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    fn implicit_submission(&self, _ctrlKey: bool, _shiftKey: bool, _altKey: bool, _metaKey: bool) {
    }
}
<commit_msg>Introduce a follow_hyperlink function to implement the "follow a hyperlink" algorithm.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::activation::Activatable;
use dom::attr::AttrValue;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLAnchorElementDerived, HTMLImageElementDerived};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::{MouseEventCast, NodeCast};
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::document::{Document, DocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::element::{Element, AttributeHandlers, ElementTypeId};
use dom::event::Event;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::WindowHelpers;

use util::str::DOMString;

use num::ToPrimitive;
use string_cache::Atom;
use url::UrlParser;

use std::default::Default;

#[dom_struct]
pub struct HTMLAnchorElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableHeap<JS<DOMTokenList>>,
}

impl HTMLAnchorElementDerived for EventTarget {
    fn is_htmlanchorelement(&self) -> bool {
        *self.type_id() ==
            EventTargetTypeId::Node(
                NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)))
    }
}

impl HTMLAnchorElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLAnchorElement {
        HTMLAnchorElement {
            htmlelement:
                HTMLElement::new_inherited(HTMLElementTypeId::HTMLAnchorElement, localName, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString,
               prefix: Option<DOMString>,
               document: &Document) -> Root<HTMLAnchorElement> {
        let element = HTMLAnchorElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLAnchorElementBinding::Wrap)
    }
}

impl<'a> VirtualMethods for &'a HTMLAnchorElement {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &&HTMLElement = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("rel") => AttrValue::from_serialized_tokenlist(value),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl<'a> HTMLAnchorElementMethods for &'a HTMLAnchorElement {
    // https://html.spec.whatwg.org/multipage/#dom-a-text
    fn Text(self) -> DOMString {
        let node = NodeCast::from_ref(self);
        node.GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/#dom-a-text
    fn SetText(self, value: DOMString) {
        let node = NodeCast::from_ref(self);
        node.SetTextContent(Some(value))
    }

    // https://html.spec.whatwg.org/multipage/#dom-a-rellist
    fn RelList(self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(ElementCast::from_ref(self), &atom!("rel"))
        })
    }
}

impl<'a> Activatable for &'a HTMLAnchorElement {
    fn as_element<'b>(&'b self) -> &'b Element {
        ElementCast::from_ref(*self)
    }

    fn is_instance_activatable(&self) -> bool {
        // https://html.spec.whatwg.org/multipage/#hyperlink
        // "a [...] element[s] with an href attribute [...] must [..] create a
        // hyperlink"
        // https://html.spec.whatwg.org/multipage/#the-a-element
        // "The activation behaviour of a elements *that create hyperlinks*"
        ElementCast::from_ref(*self).has_attribute(&atom!("href"))
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    fn pre_click_activation(&self) {
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
    fn canceled_activation(&self) {
    }

    //https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour
    fn activation_behavior(&self, event: &Event, target: &EventTarget) {
        //Step 1. If the node document is not fully active, abort.
        let doc = document_from_node(*self);
        if !doc.r().is_fully_active() {
            return;
        }
        //TODO: Step 2. Check if browsing context is specified and act accordingly.
        //Step 3. Handle <img ismap/>.
        let element = ElementCast::from_ref(*self);
        let mouse_event = MouseEventCast::to_ref(event).unwrap();
        let mut ismap_suffix = None;
        if let Some(element) = ElementCast::to_ref(target) {
            if target.is_htmlimageelement() && element.has_attribute(&atom!("ismap")) {

                let target_node = NodeCast::from_ref(element);
                let rect = window_from_node(target_node).r().content_box_query(
                    target_node.to_trusted_node_address());
                ismap_suffix = Some(
                    format!("?{},{}", mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
                                      mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px())
                )
            }
        }

        // Step 4.
        //TODO: Download the link is `download` attribute is set.
        follow_hyperlink(element, ismap_suffix);
    }

    //TODO:https://html.spec.whatwg.org/multipage/#the-a-element
    fn implicit_submission(&self, _ctrlKey: bool, _shiftKey: bool, _altKey: bool, _metaKey: bool) {
    }
}

/// https://html.spec.whatwg.org/multipage/#following-hyperlinks-2
fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<DOMString>) {
    // Step 1: replace.
    // Step 2: source browsing context.
    // Step 3: target browsing context.

    // Step 4.
    let attribute = subject.get_attribute(&ns!(""), &atom!("href")).unwrap();
    let mut href = attribute.Value();

    // Step 6.
    // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28925
    if let Some(suffix) = hyperlink_suffix {
        href.push_str(&suffix);
    }

    // Step 4-5.
    let document = document_from_node(subject);
    let url = UrlParser::new().base_url(&document.url()).parse(&href).unwrap();

    // Step 7.
    debug!("following hyperlink to {}", url.serialize());
    let window = document.window();
    window.load_url(url);
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::net::{Ipv4Addr, Ipv6Addr};
use crate::{Ipv4Network, Ipv6Network};

// TODO: These constants are true for Linux, but we have to check it for Windows and other systems
pub const IPV4_TYPE: u8 = 2;
pub const IPV6_TYPE: u8 = 3;

#[inline]
pub fn from_sql_ipv4_network(raw: &[u8]) -> Result<Ipv4Network, Box<Error + Sync + Send>> {
    assert!(raw.len() >= 7);

    if raw[0] != IPV4_TYPE {
        return Err("CIDR is not IP version 4".into());
    }

    if raw[2] != 1 {
        return Err("This field is not CIDR type, probably INET type".into());
    }

    if raw[3] != Ipv4Network::LENGTH / 8 {
        return Err(format!("CIDR is IP version 4, but have bad length '{}'", raw[3]).into());
    }

    let network_address = Ipv4Addr::new(raw[4], raw[5], raw[6], raw[7]);
    let netmask = raw[1];
    Ok(Ipv4Network::new(network_address, netmask)?)
}

#[inline]
pub fn from_sql_ipv6_network(raw: &[u8]) -> Result<Ipv6Network, Box<Error + Sync + Send>> {
    assert!(raw.len() >= 20);

    if raw[0] != IPV6_TYPE {
        return Err("CIDR is not IP version 6".into());
    }

    if raw[2] != 1 {
        return Err("This field is not CIDR type, probably INET type".into());
    }

    if raw[3] != Ipv6Network::LENGTH / 8 {
        return Err(format!("CIDR is IP version 6, but have bad length '{}'", raw[3]).into());
    }

    let mut octets = [0; 16];
    octets.copy_from_slice(&raw[4..]);
    let network_address = Ipv6Addr::from(octets);

    let netmask = raw[1];
    Ok(Ipv6Network::new(network_address, netmask)?)
}

#[inline]
pub fn to_sql_ipv4_network(network: Ipv4Network) -> [u8; 8] {
    let ip_octets = network.network_address().octets();
    let mut bytes = [0; 8];
    bytes[0] = IPV4_TYPE;
    bytes[1] = network.netmask();
    bytes[2] = 1;
    bytes[3] = Ipv4Network::LENGTH / 8;
    bytes[4..].copy_from_slice(&ip_octets);
    bytes
}

#[inline]
pub fn to_sql_ipv6_network(network: Ipv6Network) -> [u8; 20] {
    let ip_octets = network.network_address().octets();
    let mut bytes = [0; 20];
    bytes[0] = IPV6_TYPE;
    bytes[1] = network.netmask();
    bytes[2] = 1;
    bytes[3] = Ipv6Network::LENGTH / 8;
    bytes[4..].copy_from_slice(&ip_octets);
    bytes
}
<commit_msg>Code style<commit_after>use std::error::Error;
use std::net::{Ipv4Addr, Ipv6Addr};
use crate::{Ipv4Network, Ipv6Network};

// TODO: These constants are true for Linux, but we have to check it for Windows and other systems
pub const IPV4_TYPE: u8 = 2;
pub const IPV6_TYPE: u8 = 3;

#[inline]
pub fn from_sql_ipv4_network(raw: &[u8]) -> Result<Ipv4Network, Box<dyn Error + Sync + Send>> {
    assert!(raw.len() >= 7);

    if raw[0] != IPV4_TYPE {
        return Err("CIDR is not IP version 4".into());
    }

    if raw[2] != 1 {
        return Err("This field is not CIDR type, probably INET type".into());
    }

    if raw[3] != Ipv4Network::LENGTH / 8 {
        return Err(format!("CIDR is IP version 4, but have bad length '{}'", raw[3]).into());
    }

    let network_address = Ipv4Addr::new(raw[4], raw[5], raw[6], raw[7]);
    let netmask = raw[1];
    Ok(Ipv4Network::new(network_address, netmask)?)
}

#[inline]
pub fn from_sql_ipv6_network(raw: &[u8]) -> Result<Ipv6Network, Box<dyn Error + Sync + Send>> {
    assert!(raw.len() >= 20);

    if raw[0] != IPV6_TYPE {
        return Err("CIDR is not IP version 6".into());
    }

    if raw[2] != 1 {
        return Err("This field is not CIDR type, probably INET type".into());
    }

    if raw[3] != Ipv6Network::LENGTH / 8 {
        return Err(format!("CIDR is IP version 6, but have bad length '{}'", raw[3]).into());
    }

    let mut octets = [0; 16];
    octets.copy_from_slice(&raw[4..]);
    let network_address = Ipv6Addr::from(octets);

    let netmask = raw[1];
    Ok(Ipv6Network::new(network_address, netmask)?)
}

#[inline]
pub fn to_sql_ipv4_network(network: Ipv4Network) -> [u8; 8] {
    let ip_octets = network.network_address().octets();
    let mut bytes = [0; 8];
    bytes[0] = IPV4_TYPE;
    bytes[1] = network.netmask();
    bytes[2] = 1;
    bytes[3] = Ipv4Network::LENGTH / 8;
    bytes[4..].copy_from_slice(&ip_octets);
    bytes
}

#[inline]
pub fn to_sql_ipv6_network(network: Ipv6Network) -> [u8; 20] {
    let ip_octets = network.network_address().octets();
    let mut bytes = [0; 20];
    bytes[0] = IPV6_TYPE;
    bytes[1] = network.netmask();
    bytes[2] = 1;
    bytes[3] = Ipv6Network::LENGTH / 8;
    bytes[4..].copy_from_slice(&ip_octets);
    bytes
}
<|endoftext|>


<commit_before><commit_msg>Rename count_connected_puyos to count_connected<commit_after><|endoftext|>