<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 devtools_traits::{StartedTimelineMarker, TimelineMarker, TimelineMarkerType};
use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::reflector::DomObject;
use dom::document::Document;
use dom::event::{Event, EventPhase};
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
use dom::node::Node;
use dom::virtualmethods::vtable_for;
use dom::window::Window;

struct AutoDOMEventMarker {
    window: Root<Window>,
    marker: Option<StartedTimelineMarker>,
}

impl AutoDOMEventMarker {
    fn new(window: &Window) -> AutoDOMEventMarker {
        AutoDOMEventMarker {
            window: Root::from_ref(window),
            marker: Some(TimelineMarker::start("DOMEvent".to_owned())),
        }
    }
}

impl Drop for AutoDOMEventMarker {
    fn drop(&mut self) {
        self.window.emit_timeline_marker(self.marker.take().unwrap().end());
    }
}

fn handle_event(window: Option<&Window>, listener: &CompiledEventListener,
                current_target: &EventTarget, event: &Event) {
    let _marker;
    if let Some(window) = window {
        _marker = AutoDOMEventMarker::new(window);
    }

    listener.call_or_handle_event(current_target, event, Report);
}

// See dispatch_event.
// https://dom.spec.whatwg.org/#concept-event-dispatch
fn dispatch_to_listeners(event: &Event, target: &EventTarget, event_path: &[&EventTarget]) {
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    let window = match Root::downcast::<Window>(target.global()) {
        Some(window) => {
            if window.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
                Some(window)
            } else {
                None
            }
        },
        _ => None,
    };

    // Step 5.
    event.set_phase(EventPhase::Capturing);

    // Step 6.
    for object in event_path.iter().rev() {
        invoke(window.r(), object, event, Some(ListenerPhase::Capturing));
        if event.stop_propagation() {
            return;
        }
    }
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    // Step 7.
    event.set_phase(EventPhase::AtTarget);

    // Step 8.
    invoke(window.r(), target, event, None);
    if event.stop_propagation() {
        return;
    }
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    if !event.bubbles() {
        return;
    }

    // Step 9.1.
    event.set_phase(EventPhase::Bubbling);

    // Step 9.2.
    for object in event_path {
        invoke(window.r(), object, event, Some(ListenerPhase::Bubbling));
        if event.stop_propagation() {
            return;
        }
    }
}

#[derive(PartialEq)]
pub enum EventStatus {
    Canceled,
    NotCanceled
}

// https://dom.spec.whatwg.org/#concept-event-dispatch
pub fn dispatch_event(target: &EventTarget,
                      target_override: Option<&EventTarget>,
                      event: &Event) -> EventStatus {
    assert!(!event.dispatching());
    assert!(event.initialized());
    assert_eq!(event.phase(), EventPhase::None);
    assert!(event.GetCurrentTarget().is_none());

    // Step 1.
    event.mark_as_dispatching();

    // Step 2.
    event.set_target(target_override.unwrap_or(target));

    if event.stop_propagation() {
        // If the event's stop propagation flag is set, we can skip everything because
        // it prevents the calls of the invoke algorithm in the spec.

        // Step 10-12.
        event.clear_dispatching_flags();

        // Step 14.
        return event.status();
    }

    // Step 3. The "invoke" algorithm is only used on `target` separately,
    // so we don't put it in the path.
    rooted_vec!(let mut event_path);

    // Step 4.
    if let Some(target_node) = target.downcast::<Node>() {
        for ancestor in target_node.ancestors() {
            event_path.push(JS::from_ref(ancestor.upcast::<EventTarget>()));
        }
        let top_most_ancestor_or_target =
            Root::from_ref(event_path.r().last().cloned().unwrap_or(target));
        if let Some(document) = Root::downcast::<Document>(top_most_ancestor_or_target) {
            if event.type_() != atom!("load") && document.browsing_context().is_some() {
                event_path.push(JS::from_ref(document.window().upcast()));
            }
        }
    }

    // Steps 5-9. In a separate function to short-circuit various things easily.
    dispatch_to_listeners(event, target, event_path.r());

    // Default action.
    let target = event.GetTarget();
    match target {
        Some(ref target) => {
            if let Some(node) = target.downcast::<Node>() {
                let vtable = vtable_for(&node);
                vtable.handle_event(event);
            }
        }
        None => {}
    }

    // Step 10-12.
    event.clear_dispatching_flags();

    // Step 14.
    event.status()
}

// https://dom.spec.whatwg.org/#concept-event-listener-invoke
fn invoke(window: Option<&Window>,
          object: &EventTarget,
          event: &Event,
          specific_listener_phase: Option<ListenerPhase>) {
    // Step 1.
    assert!(!event.stop_propagation());

    // Steps 2-3.
    let listeners = object.get_listeners_for(&event.type_(), specific_listener_phase);

    // Step 4.
    event.set_current_target(object);

    // Step 5.
    inner_invoke(window, object, event, &listeners);

    // TODO: step 6.
}

// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
fn inner_invoke(window: Option<&Window>,
                object: &EventTarget,
                event: &Event,
                listeners: &[CompiledEventListener])
                -> bool {
    // Step 1.
    let mut found = false;

    // Step 2.
    for listener in listeners {
        // Steps 2.1 and 2.3-2.4 are not done because `listeners` contain only the
        // relevant ones for this invoke call during the dispatch algorithm.

        // Step 2.2.
        found = true;

        // TODO: step 2.5.

        // Step 2.6.
        handle_event(window, listener, object, event);
        if event.stop_immediate() {
            return found;
        }

        // TODO: step 2.7.
    }

    // Step 3.
    found
}
<commit_msg>script: Simplify EventDispatcher's default action.<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 devtools_traits::{StartedTimelineMarker, TimelineMarker, TimelineMarkerType};
use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::reflector::DomObject;
use dom::document::Document;
use dom::event::{Event, EventPhase};
use dom::eventtarget::{CompiledEventListener, EventTarget, ListenerPhase};
use dom::node::Node;
use dom::virtualmethods::vtable_for;
use dom::window::Window;

struct AutoDOMEventMarker {
    window: Root<Window>,
    marker: Option<StartedTimelineMarker>,
}

impl AutoDOMEventMarker {
    fn new(window: &Window) -> AutoDOMEventMarker {
        AutoDOMEventMarker {
            window: Root::from_ref(window),
            marker: Some(TimelineMarker::start("DOMEvent".to_owned())),
        }
    }
}

impl Drop for AutoDOMEventMarker {
    fn drop(&mut self) {
        self.window.emit_timeline_marker(self.marker.take().unwrap().end());
    }
}

fn handle_event(window: Option<&Window>, listener: &CompiledEventListener,
                current_target: &EventTarget, event: &Event) {
    let _marker;
    if let Some(window) = window {
        _marker = AutoDOMEventMarker::new(window);
    }

    listener.call_or_handle_event(current_target, event, Report);
}

// See dispatch_event.
// https://dom.spec.whatwg.org/#concept-event-dispatch
fn dispatch_to_listeners(event: &Event, target: &EventTarget, event_path: &[&EventTarget]) {
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    let window = match Root::downcast::<Window>(target.global()) {
        Some(window) => {
            if window.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
                Some(window)
            } else {
                None
            }
        },
        _ => None,
    };

    // Step 5.
    event.set_phase(EventPhase::Capturing);

    // Step 6.
    for object in event_path.iter().rev() {
        invoke(window.r(), object, event, Some(ListenerPhase::Capturing));
        if event.stop_propagation() {
            return;
        }
    }
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    // Step 7.
    event.set_phase(EventPhase::AtTarget);

    // Step 8.
    invoke(window.r(), target, event, None);
    if event.stop_propagation() {
        return;
    }
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    if !event.bubbles() {
        return;
    }

    // Step 9.1.
    event.set_phase(EventPhase::Bubbling);

    // Step 9.2.
    for object in event_path {
        invoke(window.r(), object, event, Some(ListenerPhase::Bubbling));
        if event.stop_propagation() {
            return;
        }
    }
}

#[derive(PartialEq)]
pub enum EventStatus {
    Canceled,
    NotCanceled
}

// https://dom.spec.whatwg.org/#concept-event-dispatch
pub fn dispatch_event(target: &EventTarget,
                      target_override: Option<&EventTarget>,
                      event: &Event) -> EventStatus {
    assert!(!event.dispatching());
    assert!(event.initialized());
    assert_eq!(event.phase(), EventPhase::None);
    assert!(event.GetCurrentTarget().is_none());

    // Step 1.
    event.mark_as_dispatching();

    // Step 2.
    event.set_target(target_override.unwrap_or(target));

    if event.stop_propagation() {
        // If the event's stop propagation flag is set, we can skip everything because
        // it prevents the calls of the invoke algorithm in the spec.

        // Step 10-12.
        event.clear_dispatching_flags();

        // Step 14.
        return event.status();
    }

    // Step 3. The "invoke" algorithm is only used on `target` separately,
    // so we don't put it in the path.
    rooted_vec!(let mut event_path);

    // Step 4.
    if let Some(target_node) = target.downcast::<Node>() {
        for ancestor in target_node.ancestors() {
            event_path.push(JS::from_ref(ancestor.upcast::<EventTarget>()));
        }
        let top_most_ancestor_or_target =
            Root::from_ref(event_path.r().last().cloned().unwrap_or(target));
        if let Some(document) = Root::downcast::<Document>(top_most_ancestor_or_target) {
            if event.type_() != atom!("load") && document.browsing_context().is_some() {
                event_path.push(JS::from_ref(document.window().upcast()));
            }
        }
    }

    // Steps 5-9. In a separate function to short-circuit various things easily.
    dispatch_to_listeners(event, target, event_path.r());

    // Default action.
    if let Some(target) = event.GetTarget() {
        if let Some(node) = target.downcast::<Node>() {
            let vtable = vtable_for(&node);
            vtable.handle_event(event);
        }
    }

    // Step 10-12.
    event.clear_dispatching_flags();

    // Step 14.
    event.status()
}

// https://dom.spec.whatwg.org/#concept-event-listener-invoke
fn invoke(window: Option<&Window>,
          object: &EventTarget,
          event: &Event,
          specific_listener_phase: Option<ListenerPhase>) {
    // Step 1.
    assert!(!event.stop_propagation());

    // Steps 2-3.
    let listeners = object.get_listeners_for(&event.type_(), specific_listener_phase);

    // Step 4.
    event.set_current_target(object);

    // Step 5.
    inner_invoke(window, object, event, &listeners);

    // TODO: step 6.
}

// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
fn inner_invoke(window: Option<&Window>,
                object: &EventTarget,
                event: &Event,
                listeners: &[CompiledEventListener])
                -> bool {
    // Step 1.
    let mut found = false;

    // Step 2.
    for listener in listeners {
        // Steps 2.1 and 2.3-2.4 are not done because `listeners` contain only the
        // relevant ones for this invoke call during the dispatch algorithm.

        // Step 2.2.
        found = true;

        // TODO: step 2.5.

        // Step 2.6.
        handle_event(window, listener, object, event);
        if event.stop_immediate() {
            return found;
        }

        // TODO: step 2.7.
    }

    // Step 3.
    found
}
<|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::bindings::codegen::Bindings::URLSearchParamsBinding;
use dom::bindings::codegen::Bindings::URLSearchParamsBinding::URLSearchParamsMethods;
use dom::bindings::codegen::UnionTypes::StringOrURLSearchParams::{StringOrURLSearchParams, eURLSearchParams, eString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};

use servo_util::str::DOMString;

use encoding::all::UTF_8;
use encoding::types::{EncodingRef, EncodeReplace};

use std::cell::RefCell;
use std::collections::hashmap::HashMap;
use std::fmt::radix;
use std::ascii::OwnedStrAsciiExt;

#[jstraceable]
#[must_root]
pub struct URLSearchParams {
    data: Traceable<RefCell<HashMap<DOMString, Vec<DOMString>>>>,
    reflector_: Reflector,
}

impl URLSearchParams {
    fn new_inherited() -> URLSearchParams {
        URLSearchParams {
            data: Traceable::new(RefCell::new(HashMap::new())),
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: &GlobalRef) -> Temporary<URLSearchParams> {
        reflect_dom_object(box URLSearchParams::new_inherited(), global, URLSearchParamsBinding::Wrap)
    }

    pub fn Constructor(global: &GlobalRef, init: Option<StringOrURLSearchParams>) -> Fallible<Temporary<URLSearchParams>> {
        let usp = URLSearchParams::new(global).root();
        match init {
            Some(eString(_s)) => {
                // XXXManishearth we need to parse the input here
                // http://url.spec.whatwg.org/#concept-urlencoded-parser
                // We can use rust-url's implementation here:
                // https://github.com/SimonSapin/rust-url/blob/master/form_urlencoded.rs#L29
            },
            Some(eURLSearchParams(u)) => {
                let u = u.root();
                let mut map = usp.deref().data.deref().borrow_mut();
                *map = u.data.deref().borrow().clone();
            },
            None => {}
        }
        Ok(Temporary::from_rooted(*usp))
    }
}

impl<'a> URLSearchParamsMethods for JSRef<'a, URLSearchParams> {
    fn Append(self, name: DOMString, value: DOMString) {
        self.data.deref().borrow_mut().insert_or_update_with(name, vec!(value.clone()),
                                                             |_k, v| v.push(value.clone()));
        self.update_steps();
    }

    fn Delete(self, name: DOMString) {
        self.data.deref().borrow_mut().remove(&name);
        self.update_steps();
    }

    fn Get(self, name: DOMString) -> Option<DOMString> {
        self.data.deref().borrow().find_equiv(&name).map(|v| v[0].clone())
    }

    fn Has(self, name: DOMString) -> bool {
        self.data.deref().borrow().contains_key_equiv(&name)
    }

    fn Set(self, name: DOMString, value: DOMString) {
        self.data.deref().borrow_mut().insert(name, vec!(value));
        self.update_steps();
    }
}

impl Reflectable for URLSearchParams {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

pub trait URLSearchParamsHelpers {
    fn serialize(&self, encoding: Option<EncodingRef>) -> Vec<u8>;
    fn update_steps(&self);
}

impl URLSearchParamsHelpers for URLSearchParams {
    fn serialize(&self, encoding: Option<EncodingRef>) -> Vec<u8> {
        // http://url.spec.whatwg.org/#concept-urlencoded-serializer
        fn serialize_string(value: &DOMString, encoding: EncodingRef) -> Vec<u8> {
            // http://url.spec.whatwg.org/#concept-urlencoded-byte-serializer

            let value = value.as_slice();
            // XXXManishearth should this be a strict encoding? Can unwrap()ing the result fail?
            let value = encoding.encode(value, EncodeReplace).unwrap();
            let mut buf = vec!();
            for i in value.iter() {
                let append = match *i {
                    0x20 => vec!(0x2B),
                    0x2A | 0x2D | 0x2E |
                    0x30 .. 0x39 | 0x41 .. 0x5A |
                    0x5F | 0x61..0x7A => vec!(*i),
                    a => {
                        // http://url.spec.whatwg.org/#percent-encode
                        let mut encoded = vec!(0x25); // %
                        let s = format!("{}", radix(a, 16)).into_ascii_upper();
                        let bytes = s.as_bytes();
                        encoded.push_all(bytes);
                        encoded
                    }
                };
                buf.push_all(append.as_slice());
            }
            buf
        }
        let encoding = encoding.unwrap_or(UTF_8 as EncodingRef);
        let mut buf = vec!();
        let mut first_pair = true;
        for (k, v) in self.data.deref().borrow().iter() {
            let name = serialize_string(k, encoding);
            for val in v.iter() {
                let value = serialize_string(val, encoding);
                if first_pair {
                    first_pair = false;
                } else {
                    buf.push(0x26); // &
                }
                buf.push_all(name.as_slice());
                buf.push(0x3D); // =
                buf.push_all(value.as_slice())
            }
        }
        buf
    }

    fn update_steps(&self) {
        // XXXManishearth Implement this when the URL interface is implemented
        // http://url.spec.whatwg.org/#concept-uq-update
    }
}
<commit_msg>Remove Traceable from urlsearchparams.rs<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::URLSearchParamsBinding;
use dom::bindings::codegen::Bindings::URLSearchParamsBinding::URLSearchParamsMethods;
use dom::bindings::codegen::UnionTypes::StringOrURLSearchParams::{StringOrURLSearchParams, eURLSearchParams, eString};
use dom::bindings::error::{Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};

use servo_util::str::DOMString;

use encoding::all::UTF_8;
use encoding::types::{EncodingRef, EncodeReplace};

use std::cell::RefCell;
use std::collections::hashmap::HashMap;
use std::fmt::radix;
use std::ascii::OwnedStrAsciiExt;

#[jstraceable]
#[must_root]
pub struct URLSearchParams {
    data: RefCell<HashMap<DOMString, Vec<DOMString>>>,
    reflector_: Reflector,
}

impl URLSearchParams {
    fn new_inherited() -> URLSearchParams {
        URLSearchParams {
            data: RefCell::new(HashMap::new()),
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: &GlobalRef) -> Temporary<URLSearchParams> {
        reflect_dom_object(box URLSearchParams::new_inherited(), global, URLSearchParamsBinding::Wrap)
    }

    pub fn Constructor(global: &GlobalRef, init: Option<StringOrURLSearchParams>) -> Fallible<Temporary<URLSearchParams>> {
        let usp = URLSearchParams::new(global).root();
        match init {
            Some(eString(_s)) => {
                // XXXManishearth we need to parse the input here
                // http://url.spec.whatwg.org/#concept-urlencoded-parser
                // We can use rust-url's implementation here:
                // https://github.com/SimonSapin/rust-url/blob/master/form_urlencoded.rs#L29
            },
            Some(eURLSearchParams(u)) => {
                let u = u.root();
                let mut map = usp.deref().data.borrow_mut();
                *map = u.data.borrow().clone();
            },
            None => {}
        }
        Ok(Temporary::from_rooted(*usp))
    }
}

impl<'a> URLSearchParamsMethods for JSRef<'a, URLSearchParams> {
    fn Append(self, name: DOMString, value: DOMString) {
        self.data.borrow_mut().insert_or_update_with(name, vec!(value.clone()),
                                                             |_k, v| v.push(value.clone()));
        self.update_steps();
    }

    fn Delete(self, name: DOMString) {
        self.data.borrow_mut().remove(&name);
        self.update_steps();
    }

    fn Get(self, name: DOMString) -> Option<DOMString> {
        self.data.borrow().find_equiv(&name).map(|v| v[0].clone())
    }

    fn Has(self, name: DOMString) -> bool {
        self.data.borrow().contains_key_equiv(&name)
    }

    fn Set(self, name: DOMString, value: DOMString) {
        self.data.borrow_mut().insert(name, vec!(value));
        self.update_steps();
    }
}

impl Reflectable for URLSearchParams {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

pub trait URLSearchParamsHelpers {
    fn serialize(&self, encoding: Option<EncodingRef>) -> Vec<u8>;
    fn update_steps(&self);
}

impl URLSearchParamsHelpers for URLSearchParams {
    fn serialize(&self, encoding: Option<EncodingRef>) -> Vec<u8> {
        // http://url.spec.whatwg.org/#concept-urlencoded-serializer
        fn serialize_string(value: &DOMString, encoding: EncodingRef) -> Vec<u8> {
            // http://url.spec.whatwg.org/#concept-urlencoded-byte-serializer

            let value = value.as_slice();
            // XXXManishearth should this be a strict encoding? Can unwrap()ing the result fail?
            let value = encoding.encode(value, EncodeReplace).unwrap();
            let mut buf = vec!();
            for i in value.iter() {
                let append = match *i {
                    0x20 => vec!(0x2B),
                    0x2A | 0x2D | 0x2E |
                    0x30 .. 0x39 | 0x41 .. 0x5A |
                    0x5F | 0x61..0x7A => vec!(*i),
                    a => {
                        // http://url.spec.whatwg.org/#percent-encode
                        let mut encoded = vec!(0x25); // %
                        let s = format!("{}", radix(a, 16)).into_ascii_upper();
                        let bytes = s.as_bytes();
                        encoded.push_all(bytes);
                        encoded
                    }
                };
                buf.push_all(append.as_slice());
            }
            buf
        }
        let encoding = encoding.unwrap_or(UTF_8 as EncodingRef);
        let mut buf = vec!();
        let mut first_pair = true;
        for (k, v) in self.data.borrow().iter() {
            let name = serialize_string(k, encoding);
            for val in v.iter() {
                let value = serialize_string(val, encoding);
                if first_pair {
                    first_pair = false;
                } else {
                    buf.push(0x26); // &
                }
                buf.push_all(name.as_slice());
                buf.push(0x3D); // =
                buf.push_all(value.as_slice())
            }
        }
        buf
    }

    fn update_steps(&self) {
        // XXXManishearth Implement this when the URL interface is implemented
        // http://url.spec.whatwg.org/#concept-uq-update
    }
}
<|endoftext|>


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

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body = [{}]", &body);
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id = [{}]", &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                buf.push_str(content.trim());
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        metadata.insert(matched_type.unwrap(), buf);
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::env;
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn exe_path() -> PathBuf {
        env::current_exe().unwrap()
    }

    pub fn root() -> PathBuf {
        exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

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

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat, ExtractOption, ExtractOptions};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        let mut extract_options = ExtractOptions::new();
        extract_options.add(ExtractOption::Time);
        try!(writer.set_options(&extract_options));
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body = [{}]", &body);
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id = [{}]", &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                buf.push_str(content.trim());
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        metadata.insert(matched_type.unwrap(), buf);
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::env;
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn exe_path() -> PathBuf {
        env::current_exe().unwrap()
    }

    pub fn root() -> PathBuf {
        exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

}
<|endoftext|>


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

use std::collections::HashMap;

use hcore::service::ServiceGroup;

use census::{CensusGroup, CensusMember, CensusRing, ElectionStatus};
use manager::Sys;
use manager::service::{Cfg, Pkg, ServiceBind};

#[derive(Clone, Debug, Serialize)]
pub struct Binds<'a>(HashMap<String, BindGroup<'a>>);

impl<'a> Binds<'a> {
    fn new(bindings: &[ServiceBind], census: &'a CensusRing) -> Self {
        let mut map = HashMap::default();
        for bind in bindings {
            if let Some(group) = census.census_group_for(&bind.service_group) {
                map.insert(bind.name.to_string(), BindGroup::new(group));
            }
        }
        Binds(map)
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct BindGroup<'a> {
    members: Vec<SvcMember<'a>>,
}

impl<'a> BindGroup<'a> {
    fn new(group: &'a CensusGroup) -> Self {
        BindGroup { members: group.members().iter().map(|m| SvcMember(m)).collect() }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct RenderContext<'a> {
    pub sys: &'a Sys,
    pub pkg: &'a Pkg,
    pub cfg: &'a Cfg,
    pub svc: Svc<'a>,
    pub bind: Binds<'a>,
}

impl<'a> RenderContext<'a> {
    pub fn new(service_group: &ServiceGroup,
               sys: &'a Sys,
               pkg: &'a Pkg,
               cfg: &'a Cfg,
               census: &'a CensusRing,
               bindings: &[ServiceBind])
               -> RenderContext<'a> {
        let census_group = census
            .census_group_for(&service_group)
            .expect("Census Group missing from list!");
        RenderContext {
            sys: sys,
            pkg: pkg,
            cfg: cfg,
            svc: Svc::new(census_group),
            bind: Binds::new(bindings, census),
        }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct Svc<'a> {
    pub group: &'a ServiceGroup,
    pub election_running: bool,
    pub election_no_quorum: bool,
    pub election_finished: bool,
    pub update_election_running: bool,
    pub update_election_no_quorum: bool,
    pub update_election_finished: bool,
    pub me: SvcMember<'a>,
    pub members: Vec<SvcMember<'a>>,
}

impl<'a> Svc<'a> {
    fn new(census_group: &'a CensusGroup) -> Self {
        Svc {
            group: &census_group.service_group,
            election_running: census_group.election_status == ElectionStatus::ElectionInProgress,
            election_no_quorum: census_group.election_status == ElectionStatus::ElectionNoQuorum,
            election_finished: census_group.election_status == ElectionStatus::ElectionFinished,
            update_election_running: census_group.election_status ==
                                     ElectionStatus::ElectionInProgress,
            update_election_no_quorum: census_group.election_status ==
                                       ElectionStatus::ElectionNoQuorum,
            update_election_finished: census_group.election_status ==
                                      ElectionStatus::ElectionFinished,
            me: SvcMember(census_group.me().expect("Missing 'me'")),
            members: census_group
                .members()
                .iter()
                .map(|m| SvcMember(m))
                .collect(),
        }
    }
}

/// A friendly representation of a `CensusMember` to the templating system.
#[derive(Clone, Debug, Serialize)]
pub struct SvcMember<'a>(&'a CensusMember);
<commit_msg>Add `leader` and `update_leader` fields to `templating::context::Svc`<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use hcore::service::ServiceGroup;

use census::{CensusGroup, CensusMember, CensusRing, ElectionStatus};
use manager::Sys;
use manager::service::{Cfg, Pkg, ServiceBind};

#[derive(Clone, Debug, Serialize)]
pub struct Binds<'a>(HashMap<String, BindGroup<'a>>);

impl<'a> Binds<'a> {
    fn new(bindings: &[ServiceBind], census: &'a CensusRing) -> Self {
        let mut map = HashMap::default();
        for bind in bindings {
            if let Some(group) = census.census_group_for(&bind.service_group) {
                map.insert(bind.name.to_string(), BindGroup::new(group));
            }
        }
        Binds(map)
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct BindGroup<'a> {
    members: Vec<SvcMember<'a>>,
}

impl<'a> BindGroup<'a> {
    fn new(group: &'a CensusGroup) -> Self {
        BindGroup { members: group.members().iter().map(|m| SvcMember(m)).collect() }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct RenderContext<'a> {
    pub sys: &'a Sys,
    pub pkg: &'a Pkg,
    pub cfg: &'a Cfg,
    pub svc: Svc<'a>,
    pub bind: Binds<'a>,
}

impl<'a> RenderContext<'a> {
    pub fn new(service_group: &ServiceGroup,
               sys: &'a Sys,
               pkg: &'a Pkg,
               cfg: &'a Cfg,
               census: &'a CensusRing,
               bindings: &[ServiceBind])
               -> RenderContext<'a> {
        let census_group = census
            .census_group_for(&service_group)
            .expect("Census Group missing from list!");
        RenderContext {
            sys: sys,
            pkg: pkg,
            cfg: cfg,
            svc: Svc::new(census_group),
            bind: Binds::new(bindings, census),
        }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct Svc<'a> {
    pub group: &'a ServiceGroup,
    pub election_running: bool,
    pub election_no_quorum: bool,
    pub election_finished: bool,
    pub update_election_running: bool,
    pub update_election_no_quorum: bool,
    pub update_election_finished: bool,
    pub me: SvcMember<'a>,
    pub members: Vec<SvcMember<'a>>,
    pub leader: Option<SvcMember<'a>>,
    pub update_leader: Option<SvcMember<'a>>,
}

impl<'a> Svc<'a> {
    fn new(census_group: &'a CensusGroup) -> Self {
        Svc {
            group: &census_group.service_group,
            election_running: census_group.election_status == ElectionStatus::ElectionInProgress,
            election_no_quorum: census_group.election_status == ElectionStatus::ElectionNoQuorum,
            election_finished: census_group.election_status == ElectionStatus::ElectionFinished,
            update_election_running: census_group.election_status ==
                                     ElectionStatus::ElectionInProgress,
            update_election_no_quorum: census_group.election_status ==
                                       ElectionStatus::ElectionNoQuorum,
            update_election_finished: census_group.election_status ==
                                      ElectionStatus::ElectionFinished,
            me: SvcMember(census_group.me().expect("Missing 'me'")),
            members: census_group
                .members()
                .iter()
                .map(|m| SvcMember(m))
                .collect(),
            leader: census_group.leader().map(|m| SvcMember(m)),
            update_leader: census_group.update_leader().map(|m| SvcMember(m)),
        }
    }
}

/// A friendly representation of a `CensusMember` to the templating system.
#[derive(Clone, Debug, Serialize)]
pub struct SvcMember<'a>(&'a CensusMember);
<|endoftext|>


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

use crate::{
    aws,
    cluster::Cluster,
    cluster_swarm::{cluster_swarm_kube::ClusterSwarmKube, ClusterSwarm},
    instance::{
        ApplicationConfig::{Fullnode, Validator, Vault, LSR},
        FullnodeConfig, Instance, InstanceConfig, LSRConfig, ValidatorConfig, VaultConfig,
    },
};
use anyhow::{format_err, Result};
use futures::{
    future::{join, try_join_all},
    try_join,
};
use libra_logger::info;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub struct ClusterBuilderParams {
    #[structopt(long, default_value = "1")]
    pub fullnodes_per_validator: u32,
    #[structopt(long, use_delimiter = true, default_value = "")]
    pub cfg: Vec<String>,
    #[structopt(long, parse(try_from_str), default_value = "30")]
    pub num_validators: u32,
    #[structopt(long)]
    pub enable_lsr: bool,
    #[structopt(
        long,
        help = "Backend used by lsr. Possible Values are in-memory, on-disk, vault",
        default_value = "vault"
    )]
    pub lsr_backend: String,
}

pub struct ClusterBuilder {
    current_tag: String,
    cluster_swarm: ClusterSwarmKube,
}

impl ClusterBuilder {
    pub fn new(current_tag: String, cluster_swarm: ClusterSwarmKube) -> Self {
        Self {
            current_tag,
            cluster_swarm,
        }
    }

    pub async fn setup_cluster(&self, params: &ClusterBuilderParams) -> Result<Cluster> {
        self.cluster_swarm
            .cleanup()
            .await
            .map_err(|e| format_err!("cleanup on startup failed: {}", e))?;
        let current_tag = &self.current_tag;
        info!(
            "Deploying with {} tag for validators and fullnodes",
            current_tag
        );
        let asg_name = format!(
            "{}-k8s-testnet-validators",
            self.cluster_swarm
                .get_workspace()
                .await
                .expect("Failed to get workspace")
        );
        let mut instance_count =
            params.num_validators + (params.fullnodes_per_validator * params.num_validators);
        if params.enable_lsr {
            if params.lsr_backend == "vault" {
                instance_count += params.num_validators * 2;
            } else {
                instance_count += params.num_validators;
            }
        }
        // First scale down to zero instances and wait for it to complete so that we don't schedule pods on
        // instances which are going into termination state
        aws::set_asg_size(0, 0.0, &asg_name, true, true)
            .await
            .map_err(|err| format_err!("{} scale down failed: {}", asg_name, err))?;
        // Then scale up and bring up new instances
        aws::set_asg_size(instance_count as i64, 5.0, &asg_name, true, false)
            .await
            .map_err(|err| format_err!("{} scale up failed: {}", asg_name, err))?;
        let (validators, fullnodes) = self
            .spawn_validator_and_fullnode_set(
                params.num_validators,
                params.fullnodes_per_validator,
                params.enable_lsr,
                &params.lsr_backend,
                current_tag,
                params.cfg.as_slice(),
                true,
            )
            .await
            .map_err(|e| format_err!("Failed to spawn_validator_and_fullnode_set: {}", e))?;
        let cluster = Cluster::new(validators, fullnodes);

        info!(
            "Deployed {} validators and {} fns",
            cluster.validator_instances().len(),
            cluster.fullnode_instances().len(),
        );
        Ok(cluster)
    }

    /// Creates a set of validators with the given `image_tag`
    pub async fn spawn_validator_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        enable_lsr: bool,
        lsr_backend: &str,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<Vec<Instance>> {
        let mut lsrs = vec![];
        if enable_lsr {
            if lsr_backend == "vault" {
                let mut vault_instances: Vec<_> = (0..num_validators)
                    .map(|i| {
                        let vault_config = VaultConfig {};
                        self.cluster_swarm.spawn_new_instance(
                            InstanceConfig {
                                validator_group: i,
                                application_config: Vault(vault_config),
                            },
                            delete_data,
                        )
                    })
                    .collect();
                lsrs.append(&mut vault_instances);
            }
            let mut lsr_instances: Vec<_> = (0..num_validators)
                .map(|i| {
                    let lsr_config = LSRConfig {
                        num_validators,
                        image_tag: image_tag.to_string(),
                        lsr_backend: lsr_backend.to_string(),
                    };
                    self.cluster_swarm.spawn_new_instance(
                        InstanceConfig {
                            validator_group: i,
                            application_config: LSR(lsr_config),
                        },
                        delete_data,
                    )
                })
                .collect();
            lsrs.append(&mut lsr_instances);
        }
        let validators = (0..num_validators).map(|i| {
            let validator_config = ValidatorConfig {
                num_validators,
                num_fullnodes: num_fullnodes_per_validator,
                enable_lsr,
                image_tag: image_tag.to_string(),
                config_overrides: config_overrides.to_vec(),
            };
            self.cluster_swarm.spawn_new_instance(
                InstanceConfig {
                    validator_group: i,
                    application_config: Validator(validator_config),
                },
                delete_data,
            )
        });
        let (lsrs, validators) = join(try_join_all(lsrs), try_join_all(validators)).await;
        lsrs?;
        validators
    }

    /// Creates a set of fullnodes with the given `image_tag`
    pub async fn spawn_fullnode_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<Vec<Instance>> {
        let fullnodes = (0..num_validators).flat_map(move |validator_index| {
            (0..num_fullnodes_per_validator).map(move |fullnode_index| {
                let fullnode_config = FullnodeConfig {
                    fullnode_index,
                    num_fullnodes_per_validator,
                    num_validators,
                    image_tag: image_tag.to_string(),
                    config_overrides: config_overrides.to_vec(),
                };
                self.cluster_swarm.spawn_new_instance(
                    InstanceConfig {
                        validator_group: validator_index,
                        application_config: Fullnode(fullnode_config),
                    },
                    delete_data,
                )
            })
        });
        let fullnodes = try_join_all(fullnodes).await?;
        Ok(fullnodes)
    }

    /// Creates a set of validators and fullnodes with the given parameters
    pub async fn spawn_validator_and_fullnode_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        enable_lsr: bool,
        lsr_backend: &str,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<(Vec<Instance>, Vec<Instance>)> {
        try_join!(
            self.spawn_validator_set(
                num_validators,
                num_fullnodes_per_validator,
                enable_lsr,
                lsr_backend,
                image_tag,
                config_overrides,
                delete_data,
            ),
            self.spawn_fullnode_set(
                num_validators,
                num_fullnodes_per_validator,
                image_tag,
                config_overrides,
                delete_data,
            ),
        )
    }
}
<commit_msg>aggressive prune_window in cluster-test<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    aws,
    cluster::Cluster,
    cluster_swarm::{cluster_swarm_kube::ClusterSwarmKube, ClusterSwarm},
    instance::{
        ApplicationConfig::{Fullnode, Validator, Vault, LSR},
        FullnodeConfig, Instance, InstanceConfig, LSRConfig, ValidatorConfig, VaultConfig,
    },
};
use anyhow::{format_err, Result};
use futures::{
    future::{join, try_join_all},
    try_join,
};
use libra_logger::info;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
pub struct ClusterBuilderParams {
    #[structopt(long, default_value = "1")]
    pub fullnodes_per_validator: u32,
    #[structopt(long, use_delimiter = true, default_value = "")]
    cfg: Vec<String>,
    #[structopt(long, parse(try_from_str), default_value = "30")]
    pub num_validators: u32,
    #[structopt(long)]
    pub enable_lsr: bool,
    #[structopt(
        long,
        help = "Backend used by lsr. Possible Values are in-memory, on-disk, vault",
        default_value = "vault"
    )]
    pub lsr_backend: String,
}

impl ClusterBuilderParams {
    pub fn cfg_overrides(&self) -> Vec<String> {
        // Default overrides
        let mut overrides = vec!["prune_window=50000".to_string()];

        // overrides from the command line
        overrides.extend(self.cfg.iter().cloned());

        overrides
    }
}

pub struct ClusterBuilder {
    current_tag: String,
    cluster_swarm: ClusterSwarmKube,
}

impl ClusterBuilder {
    pub fn new(current_tag: String, cluster_swarm: ClusterSwarmKube) -> Self {
        Self {
            current_tag,
            cluster_swarm,
        }
    }

    pub async fn setup_cluster(&self, params: &ClusterBuilderParams) -> Result<Cluster> {
        self.cluster_swarm
            .cleanup()
            .await
            .map_err(|e| format_err!("cleanup on startup failed: {}", e))?;
        let current_tag = &self.current_tag;
        info!(
            "Deploying with {} tag for validators and fullnodes",
            current_tag
        );
        let asg_name = format!(
            "{}-k8s-testnet-validators",
            self.cluster_swarm
                .get_workspace()
                .await
                .expect("Failed to get workspace")
        );
        let mut instance_count =
            params.num_validators + (params.fullnodes_per_validator * params.num_validators);
        if params.enable_lsr {
            if params.lsr_backend == "vault" {
                instance_count += params.num_validators * 2;
            } else {
                instance_count += params.num_validators;
            }
        }
        // First scale down to zero instances and wait for it to complete so that we don't schedule pods on
        // instances which are going into termination state
        aws::set_asg_size(0, 0.0, &asg_name, true, true)
            .await
            .map_err(|err| format_err!("{} scale down failed: {}", asg_name, err))?;
        // Then scale up and bring up new instances
        aws::set_asg_size(instance_count as i64, 5.0, &asg_name, true, false)
            .await
            .map_err(|err| format_err!("{} scale up failed: {}", asg_name, err))?;
        let (validators, fullnodes) = self
            .spawn_validator_and_fullnode_set(
                params.num_validators,
                params.fullnodes_per_validator,
                params.enable_lsr,
                &params.lsr_backend,
                current_tag,
                &params.cfg_overrides(),
                true,
            )
            .await
            .map_err(|e| format_err!("Failed to spawn_validator_and_fullnode_set: {}", e))?;
        let cluster = Cluster::new(validators, fullnodes);

        info!(
            "Deployed {} validators and {} fns",
            cluster.validator_instances().len(),
            cluster.fullnode_instances().len(),
        );
        Ok(cluster)
    }

    /// Creates a set of validators with the given `image_tag`
    pub async fn spawn_validator_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        enable_lsr: bool,
        lsr_backend: &str,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<Vec<Instance>> {
        let mut lsrs = vec![];
        if enable_lsr {
            if lsr_backend == "vault" {
                let mut vault_instances: Vec<_> = (0..num_validators)
                    .map(|i| {
                        let vault_config = VaultConfig {};
                        self.cluster_swarm.spawn_new_instance(
                            InstanceConfig {
                                validator_group: i,
                                application_config: Vault(vault_config),
                            },
                            delete_data,
                        )
                    })
                    .collect();
                lsrs.append(&mut vault_instances);
            }
            let mut lsr_instances: Vec<_> = (0..num_validators)
                .map(|i| {
                    let lsr_config = LSRConfig {
                        num_validators,
                        image_tag: image_tag.to_string(),
                        lsr_backend: lsr_backend.to_string(),
                    };
                    self.cluster_swarm.spawn_new_instance(
                        InstanceConfig {
                            validator_group: i,
                            application_config: LSR(lsr_config),
                        },
                        delete_data,
                    )
                })
                .collect();
            lsrs.append(&mut lsr_instances);
        }
        let validators = (0..num_validators).map(|i| {
            let validator_config = ValidatorConfig {
                num_validators,
                num_fullnodes: num_fullnodes_per_validator,
                enable_lsr,
                image_tag: image_tag.to_string(),
                config_overrides: config_overrides.to_vec(),
            };
            self.cluster_swarm.spawn_new_instance(
                InstanceConfig {
                    validator_group: i,
                    application_config: Validator(validator_config),
                },
                delete_data,
            )
        });
        let (lsrs, validators) = join(try_join_all(lsrs), try_join_all(validators)).await;
        lsrs?;
        validators
    }

    /// Creates a set of fullnodes with the given `image_tag`
    pub async fn spawn_fullnode_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<Vec<Instance>> {
        let fullnodes = (0..num_validators).flat_map(move |validator_index| {
            (0..num_fullnodes_per_validator).map(move |fullnode_index| {
                let fullnode_config = FullnodeConfig {
                    fullnode_index,
                    num_fullnodes_per_validator,
                    num_validators,
                    image_tag: image_tag.to_string(),
                    config_overrides: config_overrides.to_vec(),
                };
                self.cluster_swarm.spawn_new_instance(
                    InstanceConfig {
                        validator_group: validator_index,
                        application_config: Fullnode(fullnode_config),
                    },
                    delete_data,
                )
            })
        });
        let fullnodes = try_join_all(fullnodes).await?;
        Ok(fullnodes)
    }

    /// Creates a set of validators and fullnodes with the given parameters
    pub async fn spawn_validator_and_fullnode_set(
        &self,
        num_validators: u32,
        num_fullnodes_per_validator: u32,
        enable_lsr: bool,
        lsr_backend: &str,
        image_tag: &str,
        config_overrides: &[String],
        delete_data: bool,
    ) -> Result<(Vec<Instance>, Vec<Instance>)> {
        try_join!(
            self.spawn_validator_set(
                num_validators,
                num_fullnodes_per_validator,
                enable_lsr,
                lsr_backend,
                image_tag,
                config_overrides,
                delete_data,
            ),
            self.spawn_fullnode_set(
                num_validators,
                num_fullnodes_per_validator,
                image_tag,
                config_overrides,
                delete_data,
            ),
        )
    }
}
<|endoftext|>


<commit_before>// this exists primarily to test various API usages of scroll; e.g., must compile

extern crate scroll;

// #[macro_use] extern crate scroll_derive;

use std::ops::{Deref,  DerefMut};
use scroll::{ctx, Result, Cread, Pread};
use scroll::ctx::SizeWith;

pub struct Section<'a> {
    pub sectname:  [u8; 16],
    pub segname:   [u8; 16],
    pub addr:      u64,
    pub size:      u64,
    pub offset:    u32,
    pub align:     u32,
    pub reloff:    u32,
    pub nreloc:    u32,
    pub flags:     u32,
    pub data:      &'a [u8],
}

impl<'a> Section<'a> {
    pub fn name(&self) -> Result<&str> {
        self.sectname.pread::<&str>(0)
    }
    pub fn segname(&self) -> Result<&str> {
        self.segname.pread::<&str>(0)
    }
}

impl<'a> ctx::SizeWith for Section<'a> {
    type Units = usize;
    fn size_with(_ctx: &()) -> usize {
        4
    }
}

#[repr(C)]
// renable when scroll_derive Pread/Pwrite matches
//#[derive(Debug, Clone, Copy, Pread, Pwrite)]
#[derive(Debug, Clone, Copy)]
pub struct Section32 {
    pub sectname:  [u8; 16],
    pub segname:   [u8; 16],
    pub addr:      u32,
    pub size:      u32,
    pub offset:    u32,
    pub align:     u32,
    pub reloff:    u32,
    pub nreloc:    u32,
    pub flags:     u32,
    pub reserved1: u32,
    pub reserved2: u32,
}

impl<'a> ctx::TryFromCtx<'a, ()> for Section<'a> {
    type Error = scroll::Error;
    type Size = usize;
    fn try_from_ctx(_bytes: &'a [u8], _ctx: ()) -> ::std::result::Result<(Self, Self::Size), Self::Error> {
        //let section = Section::from_ctx(bytes, bytes.pread_with::<Section32>(offset, ctx)?);
        let section = unsafe { ::std::mem::uninitialized::<Section>()};
        Ok((section, ::std::mem::size_of::<Section>()))
    }
}

pub struct Segment<'a> {
    pub cmd:      u32,
    pub cmdsize:  u32,
    pub segname:  [u8; 16],
    pub vmaddr:   u64,
    pub vmsize:   u64,
    pub fileoff:  u64,
    pub filesize: u64,
    pub maxprot:  u32,
    pub initprot: u32,
    pub nsects:   u32,
    pub flags:    u32,
    pub data:     &'a [u8],
    offset:       usize,
    raw_data:     &'a [u8],
}

impl<'a> Segment<'a> {
    pub fn name(&self) -> Result<&str> {
        Ok(self.segname.pread::<&str>(0)?)
    }
    pub fn sections(&self) -> Result<Vec<Section<'a>>> {
        let nsects = self.nsects as usize;
        let mut sections = Vec::with_capacity(nsects);
        let offset = &mut (self.offset + Self::size_with(&()));
        let _size = Section::size_with(&());
        let raw_data: &'a [u8] = self.raw_data;
        for _ in 0..nsects {
            let section = raw_data.gread_with::<Section<'a>>(offset, ())?;
            sections.push(section);
            //offset += size;
        }
        Ok(sections)
    }
}

impl<'a> ctx::SizeWith for Segment<'a> {
    type Units = usize;
    fn size_with(_ctx: &()) -> usize {
        4
    }
}

pub struct Segments<'a> {
    pub segments: Vec<Segment<'a>>,
}

impl<'a> Deref for Segments<'a> {
    type Target = Vec<Segment<'a>>;
    fn deref(&self) -> &Self::Target {
        &self.segments
    }
}

impl<'a> DerefMut for Segments<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.segments
    }
}

impl<'a> Segments<'a> {
    pub fn new() -> Self {
        Segments {
            segments: Vec::new(),
        }
    }
    pub fn sections(&self) -> Result<Vec<Vec<Section<'a>>>> {
        let mut sections = Vec::new();
        for segment in &self.segments {
            sections.push(segment.sections()?);
        }
        Ok(sections)
    }
}

fn lifetime_passthrough_<'a>(segments: &Segments<'a>, section_name: &str) -> Option<&'a [u8]> {
    let segment_name = "__TEXT";
    for segment in &segments.segments {
        if let Ok(name) = segment.name() {
            println!("segment.name: {}", name);
            if name == segment_name {
                if let Ok(sections) = segment.sections() {
                    for section in sections {
                        let sname = section.name().unwrap();
                        println!("section.name: {}", sname);
                        if section_name == sname {
                            return Some(section.data);
                        }
                    }
                }
            }
        }
    }
    None
}

#[test]
fn lifetime_passthrough() {
    let segments = Segments::new();
    let _res = lifetime_passthrough_(&segments, "__text");
    assert!(true)
}

#[derive(Default)]
#[repr(packed)]
struct Foo {
    foo: i64,
    bar: u32,
}

impl scroll::ctx::FromCtx<scroll::Endian> for Foo {
    fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self {
        Foo { foo: bytes.cread_with::<i64>(0, ctx), bar: bytes.cread_with::<u32>(8, ctx) }
    }
}

impl scroll::ctx::SizeWith<scroll::Endian> for Foo {
    type Units = usize;
    fn size_with(_: &scroll::Endian) -> Self::Units {
        ::std::mem::size_of::<Foo>()
    }
}

#[test]
fn ioread_api() {
    use std::io::Cursor;
    use scroll::IOread;
    let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let mut bytes = Cursor::new(bytes_);
    let foo = bytes.ioread::<i64>().unwrap();
    let bar = bytes.ioread::<u32>().unwrap();
    assert_eq!(foo, 1);
    assert_eq!(bar, 0xbeef);
    let error = bytes.ioread::<f64>();
    assert!(error.is_err());
    let mut bytes = Cursor::new(bytes_);
    let foo_ = bytes.ioread::<Foo>().unwrap();
    assert_eq!(foo_.foo, foo);
    assert_eq!(foo_.bar, bar);
}

#[repr(packed)]
struct Bar {
    foo: i32,
    bar: u32,
}

impl scroll::ctx::FromCtx<scroll::Endian> for Bar {
    fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self {
        use scroll::Cread;
        Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) }
    }
}

#[test]
fn cread_api() {
    use scroll::Cread;
    let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let foo = bytes.cread::<u64>(0);
    let bar = bytes.cread::<u32>(8);
    assert_eq!(foo, 1);
    assert_eq!(bar, 0xbeef);
}

#[test]
fn cread_api_customtype() {
    use scroll::Cread;
    let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,];
    let bar = &bytes[..].cread::<Bar>(0);
    assert_eq!(bar.foo, -1);
    assert_eq!(bar.bar, 0xdeadbeef);
}

#[test]
#[should_panic]
fn cread_api_badindex() {
    use scroll::Cread;
    let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0xad,0xde,];
    let _foo = bytes.cread::<i64>(1_000_000);
}

#[test]
fn cwrite_api() {
    use scroll::Cwrite;
    use scroll::Cread;
    let mut bytes = [0x0; 0x10];
    bytes.cwrite::<u64>(42, 0);
    bytes.cwrite::<u32>(0xdeadbeef, 8);
    assert_eq!(bytes.cread::<u64>(0), 42);
    assert_eq!(bytes.cread::<u32>(8), 0xdeadbeef);
}

impl scroll::ctx::IntoCtx<scroll::Endian> for Bar {
    fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) {
        use scroll::Cwrite;
        bytes.cwrite_with(self.foo, 0, ctx);
        bytes.cwrite_with(self.bar, 4, ctx);
    }
}

#[test]
fn cwrite_api_customtype() {
    use scroll::{Cwrite, Cread};
    let bar = Bar { foo: -1, bar: 0xdeadbeef };
    let mut bytes = [0x0; 0x10];
    &bytes[..].cwrite::<Bar>(bar, 0);
    let bar = bytes.cread::<Bar>(0);
    assert_eq!(bar.foo, -1);
    assert_eq!(bar.bar, 0xdeadbeef);
}

<commit_msg>scroll: fix api tests for BE machines<commit_after>// this exists primarily to test various API usages of scroll; e.g., must compile

extern crate scroll;

// #[macro_use] extern crate scroll_derive;

use std::ops::{Deref,  DerefMut};
use scroll::{ctx, Result, Cread, Pread};
use scroll::ctx::SizeWith;

pub struct Section<'a> {
    pub sectname:  [u8; 16],
    pub segname:   [u8; 16],
    pub addr:      u64,
    pub size:      u64,
    pub offset:    u32,
    pub align:     u32,
    pub reloff:    u32,
    pub nreloc:    u32,
    pub flags:     u32,
    pub data:      &'a [u8],
}

impl<'a> Section<'a> {
    pub fn name(&self) -> Result<&str> {
        self.sectname.pread::<&str>(0)
    }
    pub fn segname(&self) -> Result<&str> {
        self.segname.pread::<&str>(0)
    }
}

impl<'a> ctx::SizeWith for Section<'a> {
    type Units = usize;
    fn size_with(_ctx: &()) -> usize {
        4
    }
}

#[repr(C)]
// renable when scroll_derive Pread/Pwrite matches
//#[derive(Debug, Clone, Copy, Pread, Pwrite)]
#[derive(Debug, Clone, Copy)]
pub struct Section32 {
    pub sectname:  [u8; 16],
    pub segname:   [u8; 16],
    pub addr:      u32,
    pub size:      u32,
    pub offset:    u32,
    pub align:     u32,
    pub reloff:    u32,
    pub nreloc:    u32,
    pub flags:     u32,
    pub reserved1: u32,
    pub reserved2: u32,
}

impl<'a> ctx::TryFromCtx<'a, ()> for Section<'a> {
    type Error = scroll::Error;
    type Size = usize;
    fn try_from_ctx(_bytes: &'a [u8], _ctx: ()) -> ::std::result::Result<(Self, Self::Size), Self::Error> {
        //let section = Section::from_ctx(bytes, bytes.pread_with::<Section32>(offset, ctx)?);
        let section = unsafe { ::std::mem::uninitialized::<Section>()};
        Ok((section, ::std::mem::size_of::<Section>()))
    }
}

pub struct Segment<'a> {
    pub cmd:      u32,
    pub cmdsize:  u32,
    pub segname:  [u8; 16],
    pub vmaddr:   u64,
    pub vmsize:   u64,
    pub fileoff:  u64,
    pub filesize: u64,
    pub maxprot:  u32,
    pub initprot: u32,
    pub nsects:   u32,
    pub flags:    u32,
    pub data:     &'a [u8],
    offset:       usize,
    raw_data:     &'a [u8],
}

impl<'a> Segment<'a> {
    pub fn name(&self) -> Result<&str> {
        Ok(self.segname.pread::<&str>(0)?)
    }
    pub fn sections(&self) -> Result<Vec<Section<'a>>> {
        let nsects = self.nsects as usize;
        let mut sections = Vec::with_capacity(nsects);
        let offset = &mut (self.offset + Self::size_with(&()));
        let _size = Section::size_with(&());
        let raw_data: &'a [u8] = self.raw_data;
        for _ in 0..nsects {
            let section = raw_data.gread_with::<Section<'a>>(offset, ())?;
            sections.push(section);
            //offset += size;
        }
        Ok(sections)
    }
}

impl<'a> ctx::SizeWith for Segment<'a> {
    type Units = usize;
    fn size_with(_ctx: &()) -> usize {
        4
    }
}

pub struct Segments<'a> {
    pub segments: Vec<Segment<'a>>,
}

impl<'a> Deref for Segments<'a> {
    type Target = Vec<Segment<'a>>;
    fn deref(&self) -> &Self::Target {
        &self.segments
    }
}

impl<'a> DerefMut for Segments<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.segments
    }
}

impl<'a> Segments<'a> {
    pub fn new() -> Self {
        Segments {
            segments: Vec::new(),
        }
    }
    pub fn sections(&self) -> Result<Vec<Vec<Section<'a>>>> {
        let mut sections = Vec::new();
        for segment in &self.segments {
            sections.push(segment.sections()?);
        }
        Ok(sections)
    }
}

fn lifetime_passthrough_<'a>(segments: &Segments<'a>, section_name: &str) -> Option<&'a [u8]> {
    let segment_name = "__TEXT";
    for segment in &segments.segments {
        if let Ok(name) = segment.name() {
            println!("segment.name: {}", name);
            if name == segment_name {
                if let Ok(sections) = segment.sections() {
                    for section in sections {
                        let sname = section.name().unwrap();
                        println!("section.name: {}", sname);
                        if section_name == sname {
                            return Some(section.data);
                        }
                    }
                }
            }
        }
    }
    None
}

#[test]
fn lifetime_passthrough() {
    let segments = Segments::new();
    let _res = lifetime_passthrough_(&segments, "__text");
    assert!(true)
}

#[derive(Default)]
#[repr(packed)]
struct Foo {
    foo: i64,
    bar: u32,
}

impl scroll::ctx::FromCtx<scroll::Endian> for Foo {
    fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self {
        Foo { foo: bytes.cread_with::<i64>(0, ctx), bar: bytes.cread_with::<u32>(8, ctx) }
    }
}

impl scroll::ctx::SizeWith<scroll::Endian> for Foo {
    type Units = usize;
    fn size_with(_: &scroll::Endian) -> Self::Units {
        ::std::mem::size_of::<Foo>()
    }
}

#[test]
fn ioread_api() {
    use std::io::Cursor;
    use scroll::{LE, IOread};
    let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let mut bytes = Cursor::new(bytes_);
    let foo = bytes.ioread_with::<i64>(LE).unwrap();
    let bar = bytes.ioread_with::<u32>(LE).unwrap();
    assert_eq!(foo, 1);
    assert_eq!(bar, 0xbeef);
    let error = bytes.ioread_with::<f64>(LE);
    assert!(error.is_err());
    let mut bytes = Cursor::new(bytes_);
    let foo_ = bytes.ioread_with::<Foo>(LE).unwrap();
    assert_eq!(foo_.foo, foo);
    assert_eq!(foo_.bar, bar);
}

#[repr(packed)]
struct Bar {
    foo: i32,
    bar: u32,
}

impl scroll::ctx::FromCtx<scroll::Endian> for Bar {
    fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self {
        use scroll::Cread;
        Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) }
    }
}

#[test]
fn cread_api() {
    use scroll::{LE, Cread};
    let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let foo = bytes.cread_with::<u64>(0, LE);
    let bar = bytes.cread_with::<u32>(8, LE);
    assert_eq!(foo, 1);
    assert_eq!(bar, 0xbeef);
}

#[test]
fn cread_api_customtype() {
    use scroll::{LE, Cread};
    let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,];
    let bar = &bytes[..].cread_with::<Bar>(0, LE);
    assert_eq!(bar.foo, -1);
    assert_eq!(bar.bar, 0xdeadbeef);
}

#[test]
#[should_panic]
fn cread_api_badindex() {
    use scroll::Cread;
    let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0xad,0xde,];
    let _foo = bytes.cread::<i64>(1_000_000);
}

#[test]
fn cwrite_api() {
    use scroll::Cwrite;
    use scroll::Cread;
    let mut bytes = [0x0; 16];
    bytes.cwrite::<u64>(42, 0);
    bytes.cwrite::<u32>(0xdeadbeef, 8);
    assert_eq!(bytes.cread::<u64>(0), 42);
    assert_eq!(bytes.cread::<u32>(8), 0xdeadbeef);
}

impl scroll::ctx::IntoCtx<scroll::Endian> for Bar {
    fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) {
        use scroll::Cwrite;
        bytes.cwrite_with(self.foo, 0, ctx);
        bytes.cwrite_with(self.bar, 4, ctx);
    }
}

#[test]
fn cwrite_api_customtype() {
    use scroll::{Cwrite, Cread};
    let bar = Bar { foo: -1, bar: 0xdeadbeef };
    let mut bytes = [0x0; 16];
    &bytes[..].cwrite::<Bar>(bar, 0);
    let bar = bytes.cread::<Bar>(0);
    assert_eq!(bar.foo, -1);
    assert_eq!(bar.bar, 0xdeadbeef);
}

<|endoftext|>