<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::attr::{Attr, AttrValue};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::reflector::{Reflectable};
use dom::document::Document;
use dom::element::RawLayoutElementHelpers;
use dom::element::{AttributeMutation, Element};
use dom::event::{Event};
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmlinputelement::ChangeEventRunnable;
use dom::keyboardevent::KeyboardEvent;
use dom::node::{ChildrenMutation, Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node};
use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
use msg::constellation_msg::ConstellationChan;
use range::Range;
use script_traits::ScriptMsg as ConstellationMsg;
use std::cell::Cell;
use string_cache::Atom;
use style::element_state::*;
use textinput::{KeyReaction, Lines, TextInput};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLTextAreaElement {
    htmlelement: HTMLElement,
    #[ignore_heap_size_of = "#7193"]
    textinput: DOMRefCell<TextInput<ConstellationChan<ConstellationMsg>>>,
    // https://html.spec.whatwg.org/multipage/#concept-textarea-dirty
    value_changed: Cell<bool>,
}

pub trait LayoutHTMLTextAreaElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn get_value_for_layout(self) -> String;
    #[allow(unsafe_code)]
    unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>>;
    #[allow(unsafe_code)]
    fn get_cols(self) -> u32;
    #[allow(unsafe_code)]
    fn get_rows(self) -> u32;
}

impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    unsafe fn get_value_for_layout(self) -> String {
        String::from((*self.unsafe_get()).textinput.borrow_for_layout().get_content())
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>> {
        if (*self.unsafe_get()).upcast::<Element>().get_focus_state() {
            Some((*self.unsafe_get()).textinput.borrow_for_layout()
                                      .get_absolute_selection_range())
        } else {
            None
        }
    }

    #[allow(unsafe_code)]
    fn get_cols(self) -> u32 {
        unsafe {
            (*self.upcast::<Element>().unsafe_get())
                .get_attr_for_layout(&ns!(), &atom!("cols"))
                .map_or(DEFAULT_COLS, AttrValue::as_uint)
        }
    }

    #[allow(unsafe_code)]
    fn get_rows(self) -> u32 {
        unsafe {
            (*self.upcast::<Element>().unsafe_get())
                .get_attr_for_layout(&ns!(), &atom!("rows"))
                .map_or(DEFAULT_ROWS, AttrValue::as_uint)
        }
    }
}

static DEFAULT_COLS: u32 = 20;
static DEFAULT_ROWS: u32 = 2;

impl HTMLTextAreaElement {
    fn new_inherited(localName: Atom,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLTextAreaElement {
        let chan = document.window().constellation_chan();
        HTMLTextAreaElement {
            htmlelement:
                HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
                                                      localName, prefix, document),
            textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, DOMString::new(), chan, None)),
            value_changed: Cell::new(false),
        }
    }

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

impl HTMLTextAreaElementMethods for HTMLTextAreaElement {
    // TODO A few of these attributes have default values and additional
    // constraints

    // https://html.spec.whatwg.org/multipage/#dom-textarea-cols
    make_uint_getter!(Cols, "cols", DEFAULT_COLS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-cols
    make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_getter!(Disabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#attr-fe-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#attr-fe-name
    make_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-placeholder
    make_getter!(Placeholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-placeholder
    make_setter!(SetPlaceholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/#attr-textarea-readonly
    make_bool_getter!(ReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/#attr-textarea-readonly
    make_bool_setter!(SetReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-required
    make_bool_getter!(Required, "required");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-required
    make_bool_setter!(SetRequired, "required");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-rows
    make_uint_getter!(Rows, "rows", DEFAULT_ROWS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-rows
    make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-wrap
    make_getter!(Wrap, "wrap");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-wrap
    make_setter!(SetWrap, "wrap");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-type
    fn Type(&self) -> DOMString {
        DOMString::from("textarea")
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-defaultvalue
    fn DefaultValue(&self) -> DOMString {
        self.upcast::<Node>().GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-defaultvalue
    fn SetDefaultValue(&self, value: DOMString) {
        self.upcast::<Node>().SetTextContent(Some(value));

        // if the element's dirty value flag is false, then the element's
        // raw value must be set to the value of the element's textContent IDL attribute
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-value
    fn Value(&self) -> DOMString {
        self.textinput.borrow().get_content()
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-value
    fn SetValue(&self, value: DOMString) {
        // TODO move the cursor to the end of the field
        self.textinput.borrow_mut().set_content(value);
        self.value_changed.set(true);

        self.force_relayout();
    }

    // https://html.spec.whatwg.org/multipage/#dom-lfe-labels
    fn Labels(&self) -> Root<NodeList> {
        self.upcast::<HTMLElement>().labels()
    }
}

impl HTMLTextAreaElement {
    // https://html.spec.whatwg.org/multipage/#concept-fe-mutable
    pub fn mutable(&self) -> bool {
        // https://html.spec.whatwg.org/multipage/#the-textarea-element:concept-fe-mutable
        !(self.Disabled() || self.ReadOnly())
    }
    pub fn reset(&self) {
        // https://html.spec.whatwg.org/multipage/#the-textarea-element:concept-form-reset-control
        self.SetValue(self.DefaultValue());
        self.value_changed.set(false);
    }
}

impl HTMLTextAreaElement {
    fn force_relayout(&self) {
        let doc = document_from_node(self);
        doc.content_changed(self.upcast(), NodeDamage::OtherNodeDamage)
    }
}

impl VirtualMethods for HTMLTextAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match *attr.local_name() {
            atom!("disabled") => {
                let el = self.upcast::<Element>();
                match mutation {
                    AttributeMutation::Set(_) => {
                        el.set_disabled_state(true);
                        el.set_enabled_state(false);
                    },
                    AttributeMutation::Removed => {
                        el.set_disabled_state(false);
                        el.set_enabled_state(true);
                        el.check_ancestors_disabled_state_for_form_control();
                    }
                }
            },
            _ => {},
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match *name {
            atom!("cols") => AttrValue::from_limited_u32(value, DEFAULT_COLS),
            atom!("rows") => AttrValue::from_limited_u32(value, DEFAULT_ROWS),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn unbind_from_tree(&self, context: &UnbindContext) {
        self.super_type().unwrap().unbind_from_tree(context);

        let node = self.upcast::<Node>();
        let el = self.upcast::<Element>();
        if node.ancestors().any(|ancestor| ancestor.is::<HTMLFieldSetElement>()) {
            el.check_ancestors_disabled_state_for_form_control();
        } else {
            el.check_disabled_attribute();
        }
    }

    fn children_changed(&self, mutation: &ChildrenMutation) {
        if let Some(ref s) = self.super_type() {
            s.children_changed(mutation);
        }
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // copied and modified from htmlinputelement.rs
    fn handle_event(&self, event: &Event) {
        if let Some(s) = self.super_type() {
            s.handle_event(event);
        }

        if event.type_() == atom!("click") && !event.DefaultPrevented() {
            //TODO: set the editing position for text inputs

            document_from_node(self).request_focus(self.upcast());
        } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
            if let Some(kevent) = event.downcast::<KeyboardEvent>() {
                match self.textinput.borrow_mut().handle_keydown(kevent) {
                    KeyReaction::TriggerDefaultAction => (),
                    KeyReaction::DispatchInput => {
                        self.value_changed.set(true);

                        if event.IsTrusted() {
                            ChangeEventRunnable::send(self.upcast::<Node>());
                        }

                        self.force_relayout();
                        event.PreventDefault();
                    }
                    KeyReaction::RedrawSelection => {
                        self.force_relayout();
                        event.PreventDefault();
                    }
                    KeyReaction::Nothing => (),
                }
            }
        }
    }
}

impl FormControl for HTMLTextAreaElement {}

impl Validatable for HTMLTextAreaElement {}
<commit_msg>Auto merge of #10333 - frewsxcv:textarea-links, r=mbrubeck<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::attr::{Attr, AttrValue};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::reflector::{Reflectable};
use dom::document::Document;
use dom::element::RawLayoutElementHelpers;
use dom::element::{AttributeMutation, Element};
use dom::event::{Event};
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmlinputelement::ChangeEventRunnable;
use dom::keyboardevent::KeyboardEvent;
use dom::node::{ChildrenMutation, Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node};
use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
use msg::constellation_msg::ConstellationChan;
use range::Range;
use script_traits::ScriptMsg as ConstellationMsg;
use std::cell::Cell;
use string_cache::Atom;
use style::element_state::*;
use textinput::{KeyReaction, Lines, TextInput};
use util::str::DOMString;

#[dom_struct]
pub struct HTMLTextAreaElement {
    htmlelement: HTMLElement,
    #[ignore_heap_size_of = "#7193"]
    textinput: DOMRefCell<TextInput<ConstellationChan<ConstellationMsg>>>,
    // https://html.spec.whatwg.org/multipage/#concept-textarea-dirty
    value_changed: Cell<bool>,
}

pub trait LayoutHTMLTextAreaElementHelpers {
    #[allow(unsafe_code)]
    unsafe fn get_value_for_layout(self) -> String;
    #[allow(unsafe_code)]
    unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>>;
    #[allow(unsafe_code)]
    fn get_cols(self) -> u32;
    #[allow(unsafe_code)]
    fn get_rows(self) -> u32;
}

impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    unsafe fn get_value_for_layout(self) -> String {
        String::from((*self.unsafe_get()).textinput.borrow_for_layout().get_content())
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    unsafe fn get_absolute_selection_for_layout(self) -> Option<Range<usize>> {
        if (*self.unsafe_get()).upcast::<Element>().get_focus_state() {
            Some((*self.unsafe_get()).textinput.borrow_for_layout()
                                      .get_absolute_selection_range())
        } else {
            None
        }
    }

    #[allow(unsafe_code)]
    fn get_cols(self) -> u32 {
        unsafe {
            (*self.upcast::<Element>().unsafe_get())
                .get_attr_for_layout(&ns!(), &atom!("cols"))
                .map_or(DEFAULT_COLS, AttrValue::as_uint)
        }
    }

    #[allow(unsafe_code)]
    fn get_rows(self) -> u32 {
        unsafe {
            (*self.upcast::<Element>().unsafe_get())
                .get_attr_for_layout(&ns!(), &atom!("rows"))
                .map_or(DEFAULT_ROWS, AttrValue::as_uint)
        }
    }
}

// https://html.spec.whatwg.org/multipage/#attr-textarea-cols-value
static DEFAULT_COLS: u32 = 20;

// https://html.spec.whatwg.org/multipage/#attr-textarea-rows-value
static DEFAULT_ROWS: u32 = 2;

impl HTMLTextAreaElement {
    fn new_inherited(localName: Atom,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLTextAreaElement {
        let chan = document.window().constellation_chan();
        HTMLTextAreaElement {
            htmlelement:
                HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
                                                      localName, prefix, document),
            textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, DOMString::new(), chan, None)),
            value_changed: Cell::new(false),
        }
    }

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

impl HTMLTextAreaElementMethods for HTMLTextAreaElement {
    // TODO A few of these attributes have default values and additional
    // constraints

    // https://html.spec.whatwg.org/multipage/#dom-textarea-cols
    make_uint_getter!(Cols, "cols", DEFAULT_COLS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-cols
    make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS);

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_getter!(Disabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#attr-fe-name
    make_getter!(Name, "name");

    // https://html.spec.whatwg.org/multipage/#attr-fe-name
    make_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-placeholder
    make_getter!(Placeholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-placeholder
    make_setter!(SetPlaceholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/#attr-textarea-readonly
    make_bool_getter!(ReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/#attr-textarea-readonly
    make_bool_setter!(SetReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-required
    make_bool_getter!(Required, "required");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-required
    make_bool_setter!(SetRequired, "required");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-rows
    make_uint_getter!(Rows, "rows", DEFAULT_ROWS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-rows
    make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS);

    // https://html.spec.whatwg.org/multipage/#dom-textarea-wrap
    make_getter!(Wrap, "wrap");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-wrap
    make_setter!(SetWrap, "wrap");

    // https://html.spec.whatwg.org/multipage/#dom-textarea-type
    fn Type(&self) -> DOMString {
        DOMString::from("textarea")
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-defaultvalue
    fn DefaultValue(&self) -> DOMString {
        self.upcast::<Node>().GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-defaultvalue
    fn SetDefaultValue(&self, value: DOMString) {
        self.upcast::<Node>().SetTextContent(Some(value));

        // if the element's dirty value flag is false, then the element's
        // raw value must be set to the value of the element's textContent IDL attribute
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-value
    fn Value(&self) -> DOMString {
        self.textinput.borrow().get_content()
    }

    // https://html.spec.whatwg.org/multipage/#dom-textarea-value
    fn SetValue(&self, value: DOMString) {
        // TODO move the cursor to the end of the field
        self.textinput.borrow_mut().set_content(value);
        self.value_changed.set(true);

        self.force_relayout();
    }

    // https://html.spec.whatwg.org/multipage/#dom-lfe-labels
    fn Labels(&self) -> Root<NodeList> {
        self.upcast::<HTMLElement>().labels()
    }
}

impl HTMLTextAreaElement {
    // https://html.spec.whatwg.org/multipage/#concept-fe-mutable
    pub fn mutable(&self) -> bool {
        // https://html.spec.whatwg.org/multipage/#the-textarea-element:concept-fe-mutable
        !(self.Disabled() || self.ReadOnly())
    }
    pub fn reset(&self) {
        // https://html.spec.whatwg.org/multipage/#the-textarea-element:concept-form-reset-control
        self.SetValue(self.DefaultValue());
        self.value_changed.set(false);
    }
}

impl HTMLTextAreaElement {
    fn force_relayout(&self) {
        let doc = document_from_node(self);
        doc.content_changed(self.upcast(), NodeDamage::OtherNodeDamage)
    }
}

impl VirtualMethods for HTMLTextAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match *attr.local_name() {
            atom!("disabled") => {
                let el = self.upcast::<Element>();
                match mutation {
                    AttributeMutation::Set(_) => {
                        el.set_disabled_state(true);
                        el.set_enabled_state(false);
                    },
                    AttributeMutation::Removed => {
                        el.set_disabled_state(false);
                        el.set_enabled_state(true);
                        el.check_ancestors_disabled_state_for_form_control();
                    }
                }
            },
            _ => {},
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match *name {
            atom!("cols") => AttrValue::from_limited_u32(value, DEFAULT_COLS),
            atom!("rows") => AttrValue::from_limited_u32(value, DEFAULT_ROWS),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn unbind_from_tree(&self, context: &UnbindContext) {
        self.super_type().unwrap().unbind_from_tree(context);

        let node = self.upcast::<Node>();
        let el = self.upcast::<Element>();
        if node.ancestors().any(|ancestor| ancestor.is::<HTMLFieldSetElement>()) {
            el.check_ancestors_disabled_state_for_form_control();
        } else {
            el.check_disabled_attribute();
        }
    }

    fn children_changed(&self, mutation: &ChildrenMutation) {
        if let Some(ref s) = self.super_type() {
            s.children_changed(mutation);
        }
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // copied and modified from htmlinputelement.rs
    fn handle_event(&self, event: &Event) {
        if let Some(s) = self.super_type() {
            s.handle_event(event);
        }

        if event.type_() == atom!("click") && !event.DefaultPrevented() {
            //TODO: set the editing position for text inputs

            document_from_node(self).request_focus(self.upcast());
        } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
            if let Some(kevent) = event.downcast::<KeyboardEvent>() {
                match self.textinput.borrow_mut().handle_keydown(kevent) {
                    KeyReaction::TriggerDefaultAction => (),
                    KeyReaction::DispatchInput => {
                        self.value_changed.set(true);

                        if event.IsTrusted() {
                            ChangeEventRunnable::send(self.upcast::<Node>());
                        }

                        self.force_relayout();
                        event.PreventDefault();
                    }
                    KeyReaction::RedrawSelection => {
                        self.force_relayout();
                        event.PreventDefault();
                    }
                    KeyReaction::Nothing => (),
                }
            }
        }
    }
}

impl FormControl for HTMLTextAreaElement {}

impl Validatable for HTMLTextAreaElement {}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#[cfg(test)]
mod consensusdb_test;
mod schema;

use crate::chained_bft::{
    common::Payload,
    consensus_types::{block::Block, quorum_cert::QuorumCert},
    consensusdb::schema::{
        block::BlockSchema,
        quorum_certificate::QCSchema,
        single_entry::{SingleEntryKey, SingleEntrySchema},
    },
};
use crypto::HashValue;
use failure::prelude::*;
use logger::prelude::*;
use schema::{BLOCK_CF_NAME, QC_CF_NAME, SINGLE_ENTRY_CF_NAME};
use schemadb::{
    ColumnFamilyOptions, ColumnFamilyOptionsMap, ReadOptions, SchemaBatch, DB, DEFAULT_CF_NAME,
};
use std::{collections::HashMap, iter::Iterator, path::Path, time::Instant};

type HighestTimeoutCertificates = Vec<u8>;
type ConsensusStateData = Vec<u8>;

pub struct ConsensusDB {
    db: DB,
}

impl ConsensusDB {
    pub fn new<P: AsRef<Path> + Clone>(db_root_path: P) -> Self {
        let cf_opts_map: ColumnFamilyOptionsMap = [
            (
                /* UNUSED CF = */ DEFAULT_CF_NAME,
                ColumnFamilyOptions::default(),
            ),
            (BLOCK_CF_NAME, ColumnFamilyOptions::default()),
            (QC_CF_NAME, ColumnFamilyOptions::default()),
            (SINGLE_ENTRY_CF_NAME, ColumnFamilyOptions::default()),
        ]
        .iter()
        .cloned()
        .collect();

        let path = db_root_path.as_ref().join("consensusdb");
        let instant = Instant::now();
        let db = DB::open(path.clone(), cf_opts_map).unwrap_or_else(|e| {
            panic!("ConsensusDB open failed due to {:?}, unable to continue", e)
        });

        info!(
            "Opened ConsensusDB at {:?} in {} ms",
            path,
            instant.elapsed().as_millis()
        );

        Self { db }
    }

    pub fn get_data<T: Payload>(
        &self,
    ) -> Result<(
        Option<ConsensusStateData>,
        Option<HighestTimeoutCertificates>,
        Vec<Block<T>>,
        Vec<QuorumCert>,
    )> {
        let consensus_state = self.get_state()?;
        let highest_timeout_certificates = self.get_highest_timeout_certificates()?;
        self.db
            .get::<SingleEntrySchema>(&SingleEntryKey::ConsensusState)?;
        let consensus_blocks = self
            .get_blocks()?
            .into_iter()
            .map(|(_block_hash, block_content)| block_content)
            .collect::<Vec<_>>();
        let consensus_qcs = self
            .get_quorum_certificates()?
            .into_iter()
            .map(|(_block_hash, qc)| qc)
            .collect::<Vec<_>>();
        Ok((
            consensus_state,
            highest_timeout_certificates,
            consensus_blocks,
            consensus_qcs,
        ))
    }

    pub fn save_highest_timeout_certificates(
        &self,
        highest_timeout_certificates: HighestTimeoutCertificates,
    ) -> Result<()> {
        let mut batch = SchemaBatch::new();
        batch.put::<SingleEntrySchema>(
            &SingleEntryKey::HighestTimeoutCertificates,
            &highest_timeout_certificates,
        )?;
        self.commit(batch)
    }

    pub fn save_state(&self, state: ConsensusStateData) -> Result<()> {
        let mut batch = SchemaBatch::new();
        batch.put::<SingleEntrySchema>(&SingleEntryKey::ConsensusState, &state)?;
        self.commit(batch)
    }

    pub fn save_blocks_and_quorum_certificates<T: Payload>(
        &self,
        block_data: Vec<Block<T>>,
        qc_data: Vec<QuorumCert>,
    ) -> Result<()> {
        ensure!(
            !block_data.is_empty() || !qc_data.is_empty(),
            "Consensus block and qc data is empty!"
        );
        let mut batch = SchemaBatch::new();
        block_data
            .iter()
            .map(|block| batch.put::<BlockSchema<T>>(&block.id(), block))
            .collect::<Result<()>>()?;
        qc_data
            .iter()
            .map(|qc| batch.put::<QCSchema>(&qc.certified_block_id(), qc))
            .collect::<Result<()>>()?;
        self.commit(batch)
    }

    pub fn delete_blocks_and_quorum_certificates<T: Payload>(
        &self,
        block_ids: Vec<HashValue>,
    ) -> Result<()> {
        ensure!(!block_ids.is_empty(), "Consensus block ids is empty!");
        let mut batch = SchemaBatch::new();
        block_ids
            .iter()
            .map(|hash| {
                batch.delete::<BlockSchema<T>>(hash)?;
                batch.delete::<QCSchema>(hash)
            })
            .collect::<Result<_>>()?;
        self.commit(batch)
    }

    /// Write the whole schema batch including all data necessary to mutate the ledger
    /// state of some transaction by leveraging rocksdb atomicity support.
    fn commit(&self, batch: SchemaBatch) -> Result<()> {
        self.db.write_schemas(batch)
    }

    /// Get latest timeout certificates (we only store the latest highest timeout certificates).
    fn get_highest_timeout_certificates(&self) -> Result<Option<Vec<u8>>> {
        self.db
            .get::<SingleEntrySchema>(&SingleEntryKey::HighestTimeoutCertificates)
    }

    /// Get latest consensus state (we only store the latest state).
    fn get_state(&self) -> Result<Option<Vec<u8>>> {
        self.db
            .get::<SingleEntrySchema>(&SingleEntryKey::ConsensusState)
    }

    /// Get all consensus blocks.
    fn get_blocks<T: Payload>(&self) -> Result<HashMap<HashValue, Block<T>>> {
        let mut iter = self.db.iter::<BlockSchema<T>>(ReadOptions::default())?;
        iter.seek_to_first();
        iter.collect::<Result<HashMap<HashValue, Block<T>>>>()
    }

    /// Get all consensus QCs.
    fn get_quorum_certificates(&self) -> Result<HashMap<HashValue, QuorumCert>> {
        let mut iter = self.db.iter::<QCSchema>(ReadOptions::default())?;
        iter.seek_to_first();
        iter.collect::<Result<HashMap<HashValue, QuorumCert>>>()
    }
}
<commit_msg>remove duplicate get state in get_data of consensusdb<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#[cfg(test)]
mod consensusdb_test;
mod schema;

use crate::chained_bft::{
    common::Payload,
    consensus_types::{block::Block, quorum_cert::QuorumCert},
    consensusdb::schema::{
        block::BlockSchema,
        quorum_certificate::QCSchema,
        single_entry::{SingleEntryKey, SingleEntrySchema},
    },
};
use crypto::HashValue;
use failure::prelude::*;
use logger::prelude::*;
use schema::{BLOCK_CF_NAME, QC_CF_NAME, SINGLE_ENTRY_CF_NAME};
use schemadb::{
    ColumnFamilyOptions, ColumnFamilyOptionsMap, ReadOptions, SchemaBatch, DB, DEFAULT_CF_NAME,
};
use std::{collections::HashMap, iter::Iterator, path::Path, time::Instant};

type HighestTimeoutCertificates = Vec<u8>;
type ConsensusStateData = Vec<u8>;

pub struct ConsensusDB {
    db: DB,
}

impl ConsensusDB {
    pub fn new<P: AsRef<Path> + Clone>(db_root_path: P) -> Self {
        let cf_opts_map: ColumnFamilyOptionsMap = [
            (
                /* UNUSED CF = */ DEFAULT_CF_NAME,
                ColumnFamilyOptions::default(),
            ),
            (BLOCK_CF_NAME, ColumnFamilyOptions::default()),
            (QC_CF_NAME, ColumnFamilyOptions::default()),
            (SINGLE_ENTRY_CF_NAME, ColumnFamilyOptions::default()),
        ]
        .iter()
        .cloned()
        .collect();

        let path = db_root_path.as_ref().join("consensusdb");
        let instant = Instant::now();
        let db = DB::open(path.clone(), cf_opts_map).unwrap_or_else(|e| {
            panic!("ConsensusDB open failed due to {:?}, unable to continue", e)
        });

        info!(
            "Opened ConsensusDB at {:?} in {} ms",
            path,
            instant.elapsed().as_millis()
        );

        Self { db }
    }

    pub fn get_data<T: Payload>(
        &self,
    ) -> Result<(
        Option<ConsensusStateData>,
        Option<HighestTimeoutCertificates>,
        Vec<Block<T>>,
        Vec<QuorumCert>,
    )> {
        let consensus_state = self.get_state()?;
        let highest_timeout_certificates = self.get_highest_timeout_certificates()?;
        let consensus_blocks = self
            .get_blocks()?
            .into_iter()
            .map(|(_block_hash, block_content)| block_content)
            .collect::<Vec<_>>();
        let consensus_qcs = self
            .get_quorum_certificates()?
            .into_iter()
            .map(|(_block_hash, qc)| qc)
            .collect::<Vec<_>>();
        Ok((
            consensus_state,
            highest_timeout_certificates,
            consensus_blocks,
            consensus_qcs,
        ))
    }

    pub fn save_highest_timeout_certificates(
        &self,
        highest_timeout_certificates: HighestTimeoutCertificates,
    ) -> Result<()> {
        let mut batch = SchemaBatch::new();
        batch.put::<SingleEntrySchema>(
            &SingleEntryKey::HighestTimeoutCertificates,
            &highest_timeout_certificates,
        )?;
        self.commit(batch)
    }

    pub fn save_state(&self, state: ConsensusStateData) -> Result<()> {
        let mut batch = SchemaBatch::new();
        batch.put::<SingleEntrySchema>(&SingleEntryKey::ConsensusState, &state)?;
        self.commit(batch)
    }

    pub fn save_blocks_and_quorum_certificates<T: Payload>(
        &self,
        block_data: Vec<Block<T>>,
        qc_data: Vec<QuorumCert>,
    ) -> Result<()> {
        ensure!(
            !block_data.is_empty() || !qc_data.is_empty(),
            "Consensus block and qc data is empty!"
        );
        let mut batch = SchemaBatch::new();
        block_data
            .iter()
            .map(|block| batch.put::<BlockSchema<T>>(&block.id(), block))
            .collect::<Result<()>>()?;
        qc_data
            .iter()
            .map(|qc| batch.put::<QCSchema>(&qc.certified_block_id(), qc))
            .collect::<Result<()>>()?;
        self.commit(batch)
    }

    pub fn delete_blocks_and_quorum_certificates<T: Payload>(
        &self,
        block_ids: Vec<HashValue>,
    ) -> Result<()> {
        ensure!(!block_ids.is_empty(), "Consensus block ids is empty!");
        let mut batch = SchemaBatch::new();
        block_ids
            .iter()
            .map(|hash| {
                batch.delete::<BlockSchema<T>>(hash)?;
                batch.delete::<QCSchema>(hash)
            })
            .collect::<Result<_>>()?;
        self.commit(batch)
    }

    /// Write the whole schema batch including all data necessary to mutate the ledger
    /// state of some transaction by leveraging rocksdb atomicity support.
    fn commit(&self, batch: SchemaBatch) -> Result<()> {
        self.db.write_schemas(batch)
    }

    /// Get latest timeout certificates (we only store the latest highest timeout certificates).
    fn get_highest_timeout_certificates(&self) -> Result<Option<Vec<u8>>> {
        self.db
            .get::<SingleEntrySchema>(&SingleEntryKey::HighestTimeoutCertificates)
    }

    /// Get latest consensus state (we only store the latest state).
    fn get_state(&self) -> Result<Option<Vec<u8>>> {
        self.db
            .get::<SingleEntrySchema>(&SingleEntryKey::ConsensusState)
    }

    /// Get all consensus blocks.
    fn get_blocks<T: Payload>(&self) -> Result<HashMap<HashValue, Block<T>>> {
        let mut iter = self.db.iter::<BlockSchema<T>>(ReadOptions::default())?;
        iter.seek_to_first();
        iter.collect::<Result<HashMap<HashValue, Block<T>>>>()
    }

    /// Get all consensus QCs.
    fn get_quorum_certificates(&self) -> Result<HashMap<HashValue, QuorumCert>> {
        let mut iter = self.db.iter::<QCSchema>(ReadOptions::default())?;
        iter.seek_to_first();
        iter.collect::<Result<HashMap<HashValue, QuorumCert>>>()
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate octavo;

use octavo::digest::prelude::*;
use std::collections::{VecDeque, HashSet};

type Idx = usize;
type Key = String;

fn write_hex(input: &[u8], output: &mut String) {
    use std::fmt::Write;
    output.clear();
    for byte in input {
        write!(output, "{:02x}", byte).expect("Could not write to buffer");
    }
}

fn md5(input: &[u8], mut output: &mut String) {
    let mut output_buf = vec![0u8; Md5::output_bytes()];
    let mut digest = Md5::default();
    digest.update(input);
    digest.result(&mut output_buf);
    write_hex(&output_buf, &mut output);
}

fn runs(input: &str, len: usize) -> Vec<char> {
    let mut current = None;
    let mut run = 1;
    let mut runs = Vec::new();
    for ch in input.chars() {
        if current == Some(ch) {
            run += 1;
            if run == len {
                runs.push(ch);
                run = 0;
                current = None;
            }
        } else {
            run = 1;
            current = Some(ch);
        }
    }
    runs
}

fn generate_keys(salt: &[u8], n_keys: usize) -> Vec<(Idx, Key)> {
    use std::io::Write;
    let mut keys = Vec::new();
    let mut candidates: VecDeque<(Idx, char, Key)> = VecDeque::new();
    let mut validated = HashSet::new();
    let mut input_buf: Vec<u8> = salt.to_vec();
    let mut key_buf = String::with_capacity(Md5::output_bytes() * 2);
    for idx in 0.. {
        // dump front candidate if invalidated
        if candidates.front().map(|&(cand_idx, _, _)| idx - cand_idx > 1000).unwrap_or_default() {
            candidates.pop_front();
        }
        // pop validated candidates into `keys`
        while candidates.front()
            .map(|&(cand_idx, _, _)| validated.contains(&cand_idx))
            .unwrap_or_default() {
            let (cand_idx, _, key) = candidates.pop_front().expect("`candidates` was empty");
            keys.push((cand_idx, key));
            if keys.len() >= n_keys {
                return keys;
            }
        }
        // create new key
        input_buf.truncate(salt.len());
        write!(input_buf, "{}", idx).expect("Could not write to buffer");
        md5(&input_buf, &mut key_buf);
        // handle runs of 5
        for ch in runs(&key_buf, 5) {
            // mark validated candidates
            for &(cand_idx, cand_ch, _) in &candidates {
                if cand_ch == ch {
                    validated.insert(cand_idx);
                }
            }
        }
        // handle runs of 3
        if let Some(&ch) = runs(&key_buf, 3).first() {
            candidates.push_back((idx, ch, key_buf.clone()));
        }
    }
    unreachable!()
}

fn parse_args() -> String {
    let matches = clap::App::new("Day 14")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("salt")
            .index(1)
            .short("s")
            .long("salt")
            .help("salt for generating keys")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("salt").unwrap().to_owned()
}

fn main() {
    let salt = parse_args();
    println!("Generating keys...");
    for (idx, key) in generate_keys(salt.as_bytes(), 64) {
        println!("Key at idx {}: {}", idx, key);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_keyset() {
        let keys = generate_keys(b"abc", 2);
        println!("Got keys: {:?}", keys);
        assert_eq!(keys[0].0, 39);
        assert_eq!(keys[1].0, 92);
        // assert_eq!(keys[63].0, 22728);
    }
}
<commit_msg>Day 14 part 2<commit_after>extern crate clap;
extern crate octavo;

use octavo::digest::prelude::*;
use std::collections::{VecDeque, HashSet};

type Idx = usize;
type Key = String;

fn write_hex(input: &[u8], output: &mut String) {
    use std::fmt::Write;
    output.clear();
    for byte in input {
        write!(output, "{:02x}", byte).expect("Could not write to buffer");
    }
}

fn md5(input: &[u8], mut output: &mut String) {
    let mut output_buf = vec![0u8; Md5::output_bytes()];
    let mut digest = Md5::default();
    digest.update(input);
    digest.result(&mut output_buf);
    write_hex(&output_buf, &mut output);
}

fn stretched_md5(input: &[u8], output: &mut String) {
    let mut input_buf = input.to_vec();
    for _ in 0..2017 {
        md5(&input_buf, output);
        input_buf.clear();
        input_buf.extend(output.as_bytes());
    }
}

fn runs(input: &str, len: usize) -> Vec<char> {
    let mut current = None;
    let mut run = 1;
    let mut runs = Vec::new();
    for ch in input.chars() {
        if current == Some(ch) {
            run += 1;
            if run == len {
                runs.push(ch);
                run = 0;
                current = None;
            }
        } else {
            run = 1;
            current = Some(ch);
        }
    }
    runs
}

fn generate_keys<F>(salt: &[u8], n_keys: usize, hasher: F) -> Vec<(Idx, Key)>
    where F: Fn(&[u8], &mut String)
{
    use std::io::Write;
    let mut keys = Vec::new();
    let mut candidates: VecDeque<(Idx, char, Key)> = VecDeque::new();
    let mut validated = HashSet::new();
    let mut input_buf: Vec<u8> = salt.to_vec();
    let mut key_buf = String::with_capacity(Md5::output_bytes() * 2);
    for idx in 0.. {
        // dump front candidate if invalidated
        if candidates.front().map(|&(cand_idx, _, _)| idx - cand_idx > 1000).unwrap_or_default() {
            candidates.pop_front();
        }
        // pop validated candidates into `keys`
        while candidates.front()
            .map(|&(cand_idx, _, _)| validated.contains(&cand_idx))
            .unwrap_or_default() {
            let (cand_idx, _, key) = candidates.pop_front().expect("`candidates` was empty");
            keys.push((cand_idx, key));
            print!("(Generated {})\r", keys.len());
            std::io::stdout().flush();
            if keys.len() >= n_keys {
                return keys;
            }
        }
        // create new key
        input_buf.truncate(salt.len());
        write!(input_buf, "{}", idx).expect("Could not write to buffer");
        hasher(&input_buf, &mut key_buf);
        // handle runs of 5
        for ch in runs(&key_buf, 5) {
            // mark validated candidates
            for &(cand_idx, cand_ch, _) in &candidates {
                if cand_ch == ch {
                    validated.insert(cand_idx);
                }
            }
        }
        // handle runs of 3
        if let Some(&ch) = runs(&key_buf, 3).first() {
            candidates.push_back((idx, ch, key_buf.clone()));
        }
    }
    unreachable!()
}

fn parse_args() -> String {
    let matches = clap::App::new("Day 14")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("salt")
            .index(1)
            .short("s")
            .long("salt")
            .help("salt for generating keys")
            .required(true)
            .takes_value(true))
        .get_matches();
    matches.value_of("salt").unwrap().to_owned()
}

fn main() {
    let salt = parse_args();
    println!("Generating part 1 keys...");
    for (idx, key) in generate_keys(salt.as_bytes(), 64, md5) {
        println!("Key at idx {}: {}", idx, key);
    }
    println!();
    println!("Generating part 2 keys...");
    for (idx, key) in generate_keys(salt.as_bytes(), 64, stretched_md5) {
        println!("Key at idx {}: {}", idx, key);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn example_keyset() {
        let keys = generate_keys(b"abc", 2, md5);
        println!("Got keys: {:?}", keys);
        assert_eq!(keys[0].0, 39);
        assert_eq!(keys[1].0, 92);
        // assert_eq!(keys[63].0, 22728);
    }

    #[test]
    fn stretched_hash() {
        let mut key_buf = String::with_capacity(Md5::output_bytes() * 2);
        stretched_md5(b"abc0", &mut key_buf);
        assert!(key_buf.starts_with("a107ff"));
    }
}
<|endoftext|>


<commit_before>extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate getopts;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use failure::Fail;
use org_example_ping::*;
use std::collections::HashMap;
use std::env;
use std::io::{self, BufRead, BufReader, Error, Read, Write};
use std::process::exit;
use std::sync::{Arc, RwLock};
use std::thread;
use varlink::{Call, Connection, ConnectionHandler, Listener, ServerStream, VarlinkService};

// Dynamically build the varlink rust code.
mod org_example_ping;

#[cfg(test)]
mod test;

// Main

fn print_usage(program: &str, opts: &getopts::Options) {
    let brief = format!("Usage: {} [--varlink=<address>] [--client]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut opts = getopts::Options::new();
    opts.optopt("", "varlink", "varlink address URL", "<address>");
    opts.optflag("", "client", "run in client mode");
    opts.optflag("m", "multiplex", "run in multiplex mode");
    opts.optflag("h", "help", "print this help menu");

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

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let client_mode = matches.opt_present("client");

    let ret = if client_mode {
        let connection = match matches.opt_str("varlink") {
            None => Connection::with_activate(&format!("{} --varlink=$VARLINK_ADDRESS", program))
                .unwrap(),
            Some(address) => Connection::with_address(&address).unwrap(),
        };
        run_client(&connection)
    } else if let Some(address) = matches.opt_str("varlink") {
        run_server(&address, 0, matches.opt_present("m")).map_err(|e| e.into())
    } else {
        print_usage(&program, &opts);
        eprintln!("Need varlink address in server mode.");
        exit(1);
    };
    exit(match ret {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {:?}", err);
            1
        }
    });
}

// Client

fn run_client(connection: &Arc<RwLock<varlink::Connection>>) -> Result<()> {
    {
        let mut iface = VarlinkClient::new(connection.clone());
        let ping = String::from("Test");

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let _reply = iface.upgrade().upgrade()?;
        println!("Client: upgrade()");
    }
    {
        // serve upgraded connection
        let mut conn = connection.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        writer.write_all(b"test test\nEnd\n")?;
        conn.writer = Some(writer);
        let mut buf = Vec::new();
        let mut reader = conn.reader.take().unwrap();
        if reader.read_until(b'\n', &mut buf)? == 0 {
            // incomplete data, in real life, store all bytes for the next call
            // for now just read the rest
            reader.read_to_end(&mut buf)?;
        };
        eprintln!("Client: upgraded got: {}", String::from_utf8_lossy(&buf));
        conn.reader = Some(reader);
    }
    Ok(())
}

// Server

struct MyOrgExamplePing;

impl org_example_ping::VarlinkInterface for MyOrgExamplePing {
    fn ping(&self, call: &mut Call_Ping, ping: String) -> varlink::Result<()> {
        call.reply(ping)
    }

    fn upgrade(&self, call: &mut Call_Upgrade) -> varlink::Result<()> {
        eprintln!("Server: called upgrade");
        call.to_upgraded();
        call.reply()
    }

    // An upgraded connection has its own application specific protocol.
    // Normally, there is no way back to the varlink protocol with this connection.
    fn call_upgraded(&self, call: &mut Call, bufreader: &mut BufRead) -> varlink::Result<Vec<u8>> {
        loop {
            let mut buf = String::new();
            let len = bufreader.read_line(&mut buf)?;
            if len == 0 {
                eprintln!("Server: upgraded got: none");
                // incomplete data, in real life, store all bytes for the next call
                // return Ok(buf.as_bytes().to_vec());
                return Err(varlink::ErrorKind::ConnectionClosed.into());
            }
            eprintln!("Server: upgraded got: {}", buf);

            call.writer.write_all(b"server reply: ")?;
            call.writer.write_all(buf.as_bytes())?;
            if buf.eq("End\n") {
                break;
            }
        }
        eprintln!("Server: upgraded ending");
        Ok(Vec::new())
    }
}

struct FdTracker {
    stream: Option<ServerStream>,
    buffer: Option<Vec<u8>>,
}

impl FdTracker {
    fn shutdown(&mut self) -> varlink::Result<()> {
        self.stream.as_mut().unwrap().shutdown()
    }
    fn chain_buffer(&mut self, buf: &mut Vec<u8>) {
        self.buffer.as_mut().unwrap().append(buf);
    }
    fn fill_buffer(&mut self, buf: &Vec<u8>) {
        self.buffer.as_mut().unwrap().clone_from(buf);
    }
    fn buf_as_slice(&mut self) -> &[u8] {
        self.buffer.as_mut().unwrap().as_slice()
    }
    fn write(&mut self, out: &[u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().write(out)
    }
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().read(buf)
    }
}

// listen_multiplex is just an example, if you don't want to use varlink::listen()
// and how to build your own main-loop and use the low-level varlink::handle() instead
pub fn listen_multiplex<S: ?Sized + AsRef<str>, H: ::ConnectionHandler + Send + Sync + 'static>(
    handler: H,
    address: &S,
    idle_timeout: u64,
) -> varlink::Result<()> {
    let timeout: i32 = match idle_timeout {
        0 => -1,
        n => (n * 1000) as i32,
    };

    let handler = Arc::new(handler);
    let mut fdmap: HashMap<i32, FdTracker> = HashMap::new();
    let mut fds = Vec::new();
    let mut threads = Vec::new();
    let listener = Listener::new(address)?;
    let upgraded_in_use = Arc::new(RwLock::new(0));

    listener.set_nonblocking(true)?;

    fds.push(libc::pollfd {
        fd: listener.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    loop {
        // Read activity on listening socket
        if fds[0].revents != 0 {
            let mut client = listener.accept(0)?;

            client.set_nonblocking(true)?;

            let fd = client.as_raw_fd();
            fds.push(libc::pollfd {
                fd,
                revents: 0,
                events: libc::POLLIN,
            });

            fdmap.insert(
                fd,
                FdTracker {
                    stream: Some(client),
                    buffer: Some(Vec::new()),
                },
            );
        }

        // Store which indices to remove
        let mut indices_to_remove = vec![];

        // Check client connections ...
        for (i, fds_item) in fds.iter().enumerate().skip(1) {
            if fds_item.revents != 0 {
                let mut upgraded_iface: Option<String> = None;
                let mut tracker = fdmap.get_mut(&fds_item.fd).unwrap();
                loop {
                    let mut readbuf: [u8; 8192] = [0; 8192];

                    match tracker.read(&mut readbuf) {
                        Ok(0) => {
                            let _ = tracker.shutdown();
                            indices_to_remove.push(i);
                            break;
                        }
                        Ok(len) => {
                            let mut out: Vec<u8> = Vec::new();
                            tracker.chain_buffer(&mut readbuf[0..len].to_vec());
                            eprintln!(
                                "Handling: {}",
                                String::from_utf8_lossy(&tracker.buf_as_slice())
                            );

                            match handler.handle(&mut tracker.buf_as_slice(), &mut out, None) {
                                // TODO: buffer output and write only on POLLOUT
                                Ok((unprocessed_bytes, last_iface)) => {
                                    upgraded_iface = last_iface;
                                    if !unprocessed_bytes.is_empty() {
                                        eprintln!(
                                            "Unprocessed bytes: {}",
                                            String::from_utf8_lossy(&unprocessed_bytes)
                                        );
                                    }
                                    tracker.fill_buffer(&unprocessed_bytes);

                                    if let Err(err) = tracker.write(out.as_ref()) {
                                        eprintln!("write error: {}", err);
                                        for cause in Fail::iter_causes(&err) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                }
                                Err(e) => match e.kind() {
                                    err => {
                                        eprintln!("handler error: {}", err);
                                        for cause in Fail::iter_causes(&err).skip(1) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                },
                            }
                        }
                        Err(e) => match e.kind() {
                            io::ErrorKind::WouldBlock => {
                                break;
                            }
                            _ => {
                                let _ = tracker.shutdown();
                                indices_to_remove.push(i);
                                eprintln!("IO error: {}", e);
                                break;
                            }
                        },
                    }
                }
                if upgraded_iface.is_some() {
                    eprintln!("Upgraded MODE");
                    // upgraded mode... thread away the server
                    // feed it directly with the client stream
                    // If you have a better idea, open an Issue or PR on github
                    indices_to_remove.push(i);

                    let j = thread::spawn({
                        eprintln!("upgraded thread");
                        let handler = handler.clone();
                        let mut stream = tracker.stream.take().unwrap();
                        let mut buffer = tracker.buffer.take().unwrap();
                        let mut upgraded_in_use = upgraded_in_use.clone();
                        move || {
                            let _r = stream.set_nonblocking(false);
                            let (reader, mut writer) = stream.split().unwrap();
                            let br = BufReader::new(reader);
                            let mut bufreader = Box::new(buffer.chain(br));
                            let mut upgraded_iface = upgraded_iface.take();

                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr += 1;
                            }
                            loop {
                                match handler.handle(&mut bufreader, &mut writer, upgraded_iface) {
                                    Ok((unread, iface)) => {
                                        upgraded_iface = iface;
                                        match bufreader.fill_buf() {
                                            Err(_) => {
                                                eprintln!("Upgraded end");
                                                break;
                                            }
                                            Ok(buf) => {
                                                if buf.is_empty() && unread.is_empty() => {
                                                    eprintln!("Upgraded end");
                                                    break;
                                                }

                                                if !unread.is_empty() {
                                                    eprintln!(
                                                        "Not handled bytes: {}",
                                                        String::from_utf8_lossy(&unread)
                                                    );
                                                    break;
                                                }

                                                if !buf.is_empty() {
                                                    eprintln!(
                                                        "fill_buf(): {}",
                                                        String::from_utf8_lossy(&buf)
                                                    );
                                                }
                                            }
                                        }
                                    }
                                    Err(err) => match err.kind() {
                                        varlink::ErrorKind::ConnectionClosed => {
                                            eprintln!("Upgraded end");
                                            break;
                                        }
                                        _ => {
                                            eprintln!("Upgraded end: {}", err);
                                            for cause in Fail::iter_causes(&err).skip(1) {
                                                eprintln!("  caused by: {}", cause);
                                            }
                                            break;
                                        }
                                    },
                                }
                            }
                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr -= 1;
                            }
                        }
                    });
                    threads.push(j);
                }
            }
        }

        // We can't modify the vector while we are traversing it, so update now.
        for i in indices_to_remove {
            fdmap.remove(&fds[i].fd);
            fds.remove(i);
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), (fds.len() as u32).into(), timeout) };

        if r < 0 {
            for t in threads {
                let _r = t.join();
            }
            return Err(Error::last_os_error().into());
        }

        if r == 0 && fds.len() == 1 && *upgraded_in_use.read().unwrap() == 0 {
            eprintln!("listen_multiplex: Waiting for threads to end.");
            for t in threads {
                let _r = t.join();
            }

            return Err(varlink::Error::from(varlink::ErrorKind::Timeout));
        }
    }
}

fn run_server(address: &str, timeout: u64, multiplex: bool) -> varlink::Result<()> {
    let myorgexampleping = MyOrgExamplePing;
    let myinterface = org_example_ping::new(Box::new(myorgexampleping));
    let service = VarlinkService::new(
        "org.varlink",
        "test ping service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );

    if multiplex {
        // Demonstrate a single process, single-threaded service
        listen_multiplex(service, &address, timeout)?;
    } else {
        varlink::listen(service, &address, 1, 10, timeout)?;
    }
    Ok(())
}
<commit_msg>examples/ping: fixed compile error<commit_after>extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate getopts;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use failure::Fail;
use org_example_ping::*;
use std::collections::HashMap;
use std::env;
use std::io::{self, BufRead, BufReader, Error, Read, Write};
use std::process::exit;
use std::sync::{Arc, RwLock};
use std::thread;
use varlink::{Call, Connection, ConnectionHandler, Listener, ServerStream, VarlinkService};

// Dynamically build the varlink rust code.
mod org_example_ping;

#[cfg(test)]
mod test;

// Main

fn print_usage(program: &str, opts: &getopts::Options) {
    let brief = format!("Usage: {} [--varlink=<address>] [--client]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut opts = getopts::Options::new();
    opts.optopt("", "varlink", "varlink address URL", "<address>");
    opts.optflag("", "client", "run in client mode");
    opts.optflag("m", "multiplex", "run in multiplex mode");
    opts.optflag("h", "help", "print this help menu");

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

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let client_mode = matches.opt_present("client");

    let ret = if client_mode {
        let connection = match matches.opt_str("varlink") {
            None => Connection::with_activate(&format!("{} --varlink=$VARLINK_ADDRESS", program))
                .unwrap(),
            Some(address) => Connection::with_address(&address).unwrap(),
        };
        run_client(&connection)
    } else if let Some(address) = matches.opt_str("varlink") {
        run_server(&address, 0, matches.opt_present("m")).map_err(|e| e.into())
    } else {
        print_usage(&program, &opts);
        eprintln!("Need varlink address in server mode.");
        exit(1);
    };
    exit(match ret {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {:?}", err);
            1
        }
    });
}

// Client

fn run_client(connection: &Arc<RwLock<varlink::Connection>>) -> Result<()> {
    {
        let mut iface = VarlinkClient::new(connection.clone());
        let ping = String::from("Test");

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let _reply = iface.upgrade().upgrade()?;
        println!("Client: upgrade()");
    }
    {
        // serve upgraded connection
        let mut conn = connection.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        writer.write_all(b"test test\nEnd\n")?;
        conn.writer = Some(writer);
        let mut buf = Vec::new();
        let mut reader = conn.reader.take().unwrap();
        if reader.read_until(b'\n', &mut buf)? == 0 {
            // incomplete data, in real life, store all bytes for the next call
            // for now just read the rest
            reader.read_to_end(&mut buf)?;
        };
        eprintln!("Client: upgraded got: {}", String::from_utf8_lossy(&buf));
        conn.reader = Some(reader);
    }
    Ok(())
}

// Server

struct MyOrgExamplePing;

impl org_example_ping::VarlinkInterface for MyOrgExamplePing {
    fn ping(&self, call: &mut Call_Ping, ping: String) -> varlink::Result<()> {
        call.reply(ping)
    }

    fn upgrade(&self, call: &mut Call_Upgrade) -> varlink::Result<()> {
        eprintln!("Server: called upgrade");
        call.to_upgraded();
        call.reply()
    }

    // An upgraded connection has its own application specific protocol.
    // Normally, there is no way back to the varlink protocol with this connection.
    fn call_upgraded(&self, call: &mut Call, bufreader: &mut BufRead) -> varlink::Result<Vec<u8>> {
        loop {
            let mut buf = String::new();
            let len = bufreader.read_line(&mut buf)?;
            if len == 0 {
                eprintln!("Server: upgraded got: none");
                // incomplete data, in real life, store all bytes for the next call
                // return Ok(buf.as_bytes().to_vec());
                return Err(varlink::ErrorKind::ConnectionClosed.into());
            }
            eprintln!("Server: upgraded got: {}", buf);

            call.writer.write_all(b"server reply: ")?;
            call.writer.write_all(buf.as_bytes())?;
            if buf.eq("End\n") {
                break;
            }
        }
        eprintln!("Server: upgraded ending");
        Ok(Vec::new())
    }
}

struct FdTracker {
    stream: Option<ServerStream>,
    buffer: Option<Vec<u8>>,
}

impl FdTracker {
    fn shutdown(&mut self) -> varlink::Result<()> {
        self.stream.as_mut().unwrap().shutdown()
    }
    fn chain_buffer(&mut self, buf: &mut Vec<u8>) {
        self.buffer.as_mut().unwrap().append(buf);
    }
    fn fill_buffer(&mut self, buf: &Vec<u8>) {
        self.buffer.as_mut().unwrap().clone_from(buf);
    }
    fn buf_as_slice(&mut self) -> &[u8] {
        self.buffer.as_mut().unwrap().as_slice()
    }
    fn write(&mut self, out: &[u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().write(out)
    }
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().read(buf)
    }
}

// listen_multiplex is just an example, if you don't want to use varlink::listen()
// and how to build your own main-loop and use the low-level varlink::handle() instead
pub fn listen_multiplex<S: ?Sized + AsRef<str>, H: ::ConnectionHandler + Send + Sync + 'static>(
    handler: H,
    address: &S,
    idle_timeout: u64,
) -> varlink::Result<()> {
    let timeout: i32 = match idle_timeout {
        0 => -1,
        n => (n * 1000) as i32,
    };

    let handler = Arc::new(handler);
    let mut fdmap: HashMap<i32, FdTracker> = HashMap::new();
    let mut fds = Vec::new();
    let mut threads = Vec::new();
    let listener = Listener::new(address)?;
    let upgraded_in_use = Arc::new(RwLock::new(0));

    listener.set_nonblocking(true)?;

    fds.push(libc::pollfd {
        fd: listener.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    loop {
        // Read activity on listening socket
        if fds[0].revents != 0 {
            let mut client = listener.accept(0)?;

            client.set_nonblocking(true)?;

            let fd = client.as_raw_fd();
            fds.push(libc::pollfd {
                fd,
                revents: 0,
                events: libc::POLLIN,
            });

            fdmap.insert(
                fd,
                FdTracker {
                    stream: Some(client),
                    buffer: Some(Vec::new()),
                },
            );
        }

        // Store which indices to remove
        let mut indices_to_remove = vec![];

        // Check client connections ...
        for (i, fds_item) in fds.iter().enumerate().skip(1) {
            if fds_item.revents != 0 {
                let mut upgraded_iface: Option<String> = None;
                let mut tracker = fdmap.get_mut(&fds_item.fd).unwrap();
                loop {
                    let mut readbuf: [u8; 8192] = [0; 8192];

                    match tracker.read(&mut readbuf) {
                        Ok(0) => {
                            let _ = tracker.shutdown();
                            indices_to_remove.push(i);
                            break;
                        }
                        Ok(len) => {
                            let mut out: Vec<u8> = Vec::new();
                            tracker.chain_buffer(&mut readbuf[0..len].to_vec());
                            eprintln!(
                                "Handling: {}",
                                String::from_utf8_lossy(&tracker.buf_as_slice())
                            );

                            match handler.handle(&mut tracker.buf_as_slice(), &mut out, None) {
                                // TODO: buffer output and write only on POLLOUT
                                Ok((unprocessed_bytes, last_iface)) => {
                                    upgraded_iface = last_iface;
                                    if !unprocessed_bytes.is_empty() {
                                        eprintln!(
                                            "Unprocessed bytes: {}",
                                            String::from_utf8_lossy(&unprocessed_bytes)
                                        );
                                    }
                                    tracker.fill_buffer(&unprocessed_bytes);

                                    if let Err(err) = tracker.write(out.as_ref()) {
                                        eprintln!("write error: {}", err);
                                        for cause in Fail::iter_causes(&err) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                }
                                Err(e) => match e.kind() {
                                    err => {
                                        eprintln!("handler error: {}", err);
                                        for cause in Fail::iter_causes(&err).skip(1) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                },
                            }
                        }
                        Err(e) => match e.kind() {
                            io::ErrorKind::WouldBlock => {
                                break;
                            }
                            _ => {
                                let _ = tracker.shutdown();
                                indices_to_remove.push(i);
                                eprintln!("IO error: {}", e);
                                break;
                            }
                        },
                    }
                }
                if upgraded_iface.is_some() {
                    eprintln!("Upgraded MODE");
                    // upgraded mode... thread away the server
                    // feed it directly with the client stream
                    // If you have a better idea, open an Issue or PR on github
                    indices_to_remove.push(i);

                    let j = thread::spawn({
                        eprintln!("upgraded thread");
                        let handler = handler.clone();
                        let mut stream = tracker.stream.take().unwrap();
                        let mut buffer = tracker.buffer.take().unwrap();
                        let mut upgraded_in_use = upgraded_in_use.clone();
                        move || {
                            let _r = stream.set_nonblocking(false);
                            let (reader, mut writer) = stream.split().unwrap();
                            let br = BufReader::new(reader);
                            let mut bufreader = Box::new(buffer.chain(br));
                            let mut upgraded_iface = upgraded_iface.take();

                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr += 1;
                            }
                            loop {
                                match handler.handle(&mut bufreader, &mut writer, upgraded_iface) {
                                    Ok((unread, iface)) => {
                                        upgraded_iface = iface;
                                        match bufreader.fill_buf() {
                                            Err(_) => {
                                                eprintln!("Upgraded end");
                                                break;
                                            }
                                            Ok(buf) => {
                                                if buf.is_empty() && unread.is_empty() {
                                                    eprintln!("Upgraded end");
                                                    break;
                                                }

                                                if !unread.is_empty() {
                                                    eprintln!(
                                                        "Not handled bytes: {}",
                                                        String::from_utf8_lossy(&unread)
                                                    );
                                                    break;
                                                }

                                                if !buf.is_empty() {
                                                    eprintln!(
                                                        "fill_buf(): {}",
                                                        String::from_utf8_lossy(&buf)
                                                    );
                                                }
                                            }
                                        }
                                    }
                                    Err(err) => match err.kind() {
                                        varlink::ErrorKind::ConnectionClosed => {
                                            eprintln!("Upgraded end");
                                            break;
                                        }
                                        _ => {
                                            eprintln!("Upgraded end: {}", err);
                                            for cause in Fail::iter_causes(&err).skip(1) {
                                                eprintln!("  caused by: {}", cause);
                                            }
                                            break;
                                        }
                                    },
                                }
                            }
                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr -= 1;
                            }
                        }
                    });
                    threads.push(j);
                }
            }
        }

        // We can't modify the vector while we are traversing it, so update now.
        for i in indices_to_remove {
            fdmap.remove(&fds[i].fd);
            fds.remove(i);
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), (fds.len() as u32).into(), timeout) };

        if r < 0 {
            for t in threads {
                let _r = t.join();
            }
            return Err(Error::last_os_error().into());
        }

        if r == 0 && fds.len() == 1 && *upgraded_in_use.read().unwrap() == 0 {
            eprintln!("listen_multiplex: Waiting for threads to end.");
            for t in threads {
                let _r = t.join();
            }

            return Err(varlink::Error::from(varlink::ErrorKind::Timeout));
        }
    }
}

fn run_server(address: &str, timeout: u64, multiplex: bool) -> varlink::Result<()> {
    let myorgexampleping = MyOrgExamplePing;
    let myinterface = org_example_ping::new(Box::new(myorgexampleping));
    let service = VarlinkService::new(
        "org.varlink",
        "test ping service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );

    if multiplex {
        // Demonstrate a single process, single-threaded service
        listen_multiplex(service, &address, timeout)?;
    } else {
        varlink::listen(service, &address, 1, 10, timeout)?;
    }
    Ok(())
}
<|endoftext|>