<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::StorageBinding;
use dom::bindings::codegen::Bindings::StorageBinding::StorageMethods;
use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::storageevent::StorageEvent;
use dom::window::Window;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::IpcSend;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
use script_thread::{Runnable, ScriptThread};
use script_traits::ScriptMsg;
use servo_url::ServoUrl;
use task_source::TaskSource;

#[dom_struct]
pub struct Storage {
    reflector_: Reflector,
    storage_type: StorageType
}

impl Storage {
    fn new_inherited(storage_type: StorageType) -> Storage {
        Storage {
            reflector_: Reflector::new(),
            storage_type: storage_type
        }
    }

    pub fn new(global: &Window, storage_type: StorageType) -> Root<Storage> {
        reflect_dom_object(box Storage::new_inherited(storage_type), global, StorageBinding::Wrap)
    }

    fn get_url(&self) -> ServoUrl {
        self.global().get_url()
    }

    fn get_storage_thread(&self) -> IpcSender<StorageThreadMsg> {
        self.global().resource_threads().sender()
    }

}

impl StorageMethods for Storage {
    // https://html.spec.whatwg.org/multipage/#dom-storage-length
    fn Length(&self) -> u32 {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Length(sender, self.get_url(), self.storage_type)).unwrap();
        receiver.recv().unwrap() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-key
    fn Key(&self, index: u32) -> Option<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread()
            .send(StorageThreadMsg::Key(sender, self.get_url(), self.storage_type, index))
            .unwrap();
        receiver.recv().unwrap().map(DOMString::from)
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-getitem
    fn GetItem(&self, name: DOMString) -> Option<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);

        let msg = StorageThreadMsg::GetItem(sender, self.get_url(), self.storage_type, name);
        self.get_storage_thread().send(msg).unwrap();
        receiver.recv().unwrap().map(DOMString::from)
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-setitem
    fn SetItem(&self, name: DOMString, value: DOMString) -> ErrorResult {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);
        let value = String::from(value);

        let msg = StorageThreadMsg::SetItem(sender, self.get_url(), self.storage_type, name.clone(), value.clone());
        self.get_storage_thread().send(msg).unwrap();
        match receiver.recv().unwrap() {
            Err(_) => Err(Error::QuotaExceeded),
            Ok((changed, old_value)) => {
              if changed {
                  self.broadcast_change_notification(Some(name), old_value, Some(value));
              }
              Ok(())
            }
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-removeitem
    fn RemoveItem(&self, name: DOMString) {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);

        let msg = StorageThreadMsg::RemoveItem(sender, self.get_url(), self.storage_type, name.clone());
        self.get_storage_thread().send(msg).unwrap();
        if let Some(old_value) = receiver.recv().unwrap() {
            self.broadcast_change_notification(Some(name), Some(old_value), None);
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-clear
    fn Clear(&self) {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Clear(sender, self.get_url(), self.storage_type)).unwrap();
        if receiver.recv().unwrap() {
            self.broadcast_change_notification(None, None, None);
        }
    }

    // https://html.spec.whatwg.org/multipage/#the-storage-interface:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Keys(sender, self.get_url(), self.storage_type)).unwrap();
        receiver.recv()
                .unwrap()
                .into_iter()
                .map(DOMString::from)
                .collect()
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString) -> Option<DOMString> {
        self.GetItem(name)
    }

    fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult {
        self.SetItem(name, value)
    }

    fn NamedDeleter(&self, name: DOMString) {
        self.RemoveItem(name);
    }
}

impl Storage {
    /// https://html.spec.whatwg.org/multipage/#send-a-storage-notification
    fn broadcast_change_notification(&self, key: Option<String>, old_value: Option<String>,
                                     new_value: Option<String>) {
        let storage = self.storage_type;
        let url = self.get_url();
        let msg = ScriptMsg::BroadcastStorageEvent(storage, url, key, old_value, new_value);
        self.global().script_to_constellation_chan().send(msg).unwrap();
    }

    /// https://html.spec.whatwg.org/multipage/#send-a-storage-notification
    pub fn queue_storage_event(&self, url: ServoUrl,
                               key: Option<String>, old_value: Option<String>, new_value: Option<String>) {
        let global = self.global();
        let window = global.as_window();
        let task_source = window.dom_manipulation_task_source();
        let trusted_storage = Trusted::new(self);
        task_source
            .queue(
                box StorageEventRunnable::new(trusted_storage, url, key, old_value, new_value), &global)
            .unwrap();
    }
}

pub struct StorageEventRunnable {
    element: Trusted<Storage>,
    url: ServoUrl,
    key: Option<String>,
    old_value: Option<String>,
    new_value: Option<String>
}

impl StorageEventRunnable {
    fn new(storage: Trusted<Storage>, url: ServoUrl,
           key: Option<String>, old_value: Option<String>, new_value: Option<String>) -> StorageEventRunnable {
        StorageEventRunnable { element: storage, url: url, key: key, old_value: old_value, new_value: new_value }
    }
}

impl Runnable for StorageEventRunnable {
    fn main_thread_handler(self: Box<StorageEventRunnable>, _: &ScriptThread) {
        let this = *self;
        let storage = this.element.root();
        let global = storage.global();
        let window = global.as_window();

        let storage_event = StorageEvent::new(
            &window,
            atom!("storage"),
            EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
            this.key.map(DOMString::from), this.old_value.map(DOMString::from), this.new_value.map(DOMString::from),
            DOMString::from(this.url.into_string()),
            Some(&storage)
        );

        storage_event.upcast::<Event>().fire(window.upcast());
    }
}
<commit_msg>Clean up Storage::queue_storage_event<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::StorageBinding;
use dom::bindings::codegen::Bindings::StorageBinding::StorageMethods;
use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::storageevent::StorageEvent;
use dom::window::Window;
use dom_struct::dom_struct;
use ipc_channel::ipc::{self, IpcSender};
use net_traits::IpcSend;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
use script_thread::Runnable;
use script_traits::ScriptMsg;
use servo_url::ServoUrl;
use task_source::TaskSource;

#[dom_struct]
pub struct Storage {
    reflector_: Reflector,
    storage_type: StorageType
}

impl Storage {
    fn new_inherited(storage_type: StorageType) -> Storage {
        Storage {
            reflector_: Reflector::new(),
            storage_type: storage_type
        }
    }

    pub fn new(global: &Window, storage_type: StorageType) -> Root<Storage> {
        reflect_dom_object(box Storage::new_inherited(storage_type), global, StorageBinding::Wrap)
    }

    fn get_url(&self) -> ServoUrl {
        self.global().get_url()
    }

    fn get_storage_thread(&self) -> IpcSender<StorageThreadMsg> {
        self.global().resource_threads().sender()
    }

}

impl StorageMethods for Storage {
    // https://html.spec.whatwg.org/multipage/#dom-storage-length
    fn Length(&self) -> u32 {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Length(sender, self.get_url(), self.storage_type)).unwrap();
        receiver.recv().unwrap() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-key
    fn Key(&self, index: u32) -> Option<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread()
            .send(StorageThreadMsg::Key(sender, self.get_url(), self.storage_type, index))
            .unwrap();
        receiver.recv().unwrap().map(DOMString::from)
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-getitem
    fn GetItem(&self, name: DOMString) -> Option<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);

        let msg = StorageThreadMsg::GetItem(sender, self.get_url(), self.storage_type, name);
        self.get_storage_thread().send(msg).unwrap();
        receiver.recv().unwrap().map(DOMString::from)
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-setitem
    fn SetItem(&self, name: DOMString, value: DOMString) -> ErrorResult {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);
        let value = String::from(value);

        let msg = StorageThreadMsg::SetItem(sender, self.get_url(), self.storage_type, name.clone(), value.clone());
        self.get_storage_thread().send(msg).unwrap();
        match receiver.recv().unwrap() {
            Err(_) => Err(Error::QuotaExceeded),
            Ok((changed, old_value)) => {
              if changed {
                  self.broadcast_change_notification(Some(name), old_value, Some(value));
              }
              Ok(())
            }
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-removeitem
    fn RemoveItem(&self, name: DOMString) {
        let (sender, receiver) = ipc::channel().unwrap();
        let name = String::from(name);

        let msg = StorageThreadMsg::RemoveItem(sender, self.get_url(), self.storage_type, name.clone());
        self.get_storage_thread().send(msg).unwrap();
        if let Some(old_value) = receiver.recv().unwrap() {
            self.broadcast_change_notification(Some(name), Some(old_value), None);
        }
    }

    // https://html.spec.whatwg.org/multipage/#dom-storage-clear
    fn Clear(&self) {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Clear(sender, self.get_url(), self.storage_type)).unwrap();
        if receiver.recv().unwrap() {
            self.broadcast_change_notification(None, None, None);
        }
    }

    // https://html.spec.whatwg.org/multipage/#the-storage-interface:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        let (sender, receiver) = ipc::channel().unwrap();

        self.get_storage_thread().send(StorageThreadMsg::Keys(sender, self.get_url(), self.storage_type)).unwrap();
        receiver.recv()
                .unwrap()
                .into_iter()
                .map(DOMString::from)
                .collect()
    }

    // check-tidy: no specs after this line
    fn NamedGetter(&self, name: DOMString) -> Option<DOMString> {
        self.GetItem(name)
    }

    fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult {
        self.SetItem(name, value)
    }

    fn NamedDeleter(&self, name: DOMString) {
        self.RemoveItem(name);
    }
}

impl Storage {
    /// https://html.spec.whatwg.org/multipage/#send-a-storage-notification
    fn broadcast_change_notification(&self, key: Option<String>, old_value: Option<String>,
                                     new_value: Option<String>) {
        let storage = self.storage_type;
        let url = self.get_url();
        let msg = ScriptMsg::BroadcastStorageEvent(storage, url, key, old_value, new_value);
        self.global().script_to_constellation_chan().send(msg).unwrap();
    }

    /// https://html.spec.whatwg.org/multipage/#send-a-storage-notification
    pub fn queue_storage_event(
        &self,
        url: ServoUrl,
        key: Option<String>,
        old_value: Option<String>,
        new_value: Option<String>,
    ) {
        let global = self.global();
        global.as_window().dom_manipulation_task_source().queue(
            box StorageEventRunnable {
                element: Trusted::new(self),
                url,
                key,
                old_value,
                new_value,
            },
            global.upcast(),
        ).unwrap();
        struct StorageEventRunnable {
            element: Trusted<Storage>,
            url: ServoUrl,
            key: Option<String>,
            old_value: Option<String>,
            new_value: Option<String>
        }
        impl Runnable for StorageEventRunnable {
            fn handler(self: Box<Self>) {
                let this = *self;
                let storage = this.element.root();
                let global = storage.global();
                let storage_event = StorageEvent::new(
                    global.as_window(),
                    atom!("storage"),
                    EventBubbles::DoesNotBubble,
                    EventCancelable::NotCancelable,
                    this.key.map(DOMString::from),
                    this.old_value.map(DOMString::from),
                    this.new_value.map(DOMString::from),
                    DOMString::from(this.url.into_string()),
                    Some(&storage)
                );
                storage_event.upcast::<Event>().fire(global.upcast());
            }
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

// # Mutable Iterators

// ## Part 1

// To start out our introduction to constructing mutable iterators, we will use
// a simple data structure consisting nothing but a vector of integers.  But
// these are our integers, so they are special, which is why we will now call
// them out `SpecialIntegers` type.

struct SpecialIntegers(Vec<u32>);

// To implement an iterator, we essentially need to tell Rust two things:
//   a. What kind of elements will we be iterating over.  When iterating
//      over our structure, we wil want our special integers (which are `u32`).
//   b. What is the next element when we are iterating.
//
// We will therefore have the responsibility of keeping track of where we are at
// while iterating.  We will do this by constructing what is
// oftern called an "iterator interface".  To keep track of our iteration, we will
// need our `SpecialIntegers` data structure and a cursor to keep track of where
// we are.

struct SpecialIntegersIterator<'a> {
    data: &'a mut SpecialIntegers,
    cursor: usize,
}

// We now need to tell rust how to get the next element in our iterator, which
// we accomplish by implementing the `Iterator` trait on our interator inteface.
// There is a problem that we will run into however.  Our iterator interface
// has a mutable borrow of our `SpecialIntegers` data structure.  So while we
// are holding this mutable borrow, we aren't technically allowed to give a mutable
// reference to elements in our list.  We therefore will be required to use unsafe.
// But before we do so, we should stop and think for a second: is what I am doing
// actually safe?

impl<'a> Iterator for SpecialIntegersIterator<'a> {
    // The type of object we will be iterating over
    type Item = &'a mut u32;
    fn next(&mut self) -> Option<&'a mut u32> {
        unimplemented!();
    }
}

// However, before we will be able to use this iterator interface, unless you
// are planning on creating a brand new `SpecialIntegersIterator` structure
// each you want to use an iterator, we need to tell Rust how to make this structure
// for wehenever we need an iterator.  We do this by implementing the `IntoIterator`
// trait.  Which, less, tells rust how to make an iterator interface by calling
// `iter_mut`.

impl<'a> IntoIterator for &'a mut SpecialIntegers {
    type Item = &'a mut u32;
    type IntoIter = SpecialIntegersIterator<'a>;

    fn into_iter(self) -> SpecialIntegersIterator<'a> {
        unimplemented!();
    }
}

// ## Part 2

// We will implement a simple tree that could, say, represent a simple DOM
// structure.  You may notice that we don't actually allow for text in our
// simple representation, but this isn't important here, what we really want
// to investigate is how we would implement a mutable iterator using our custom
// data structure.  Our DOM structure will only allow for four different types
// of nodes: Header, Paragraph, Text, and Images.

enum NodeType {
    Header,
    Paragraph,
    Text,
    Image,
}

// Each node with have a list of `children` nodes and a tag for the respective
// DOM element type.

struct DomNode {
    children: Vec<DomNode>,
    node_type: NodeType
}

// To implement an iterator, we essentially just need to tell Rust two things:
//   a. What kind of elements will we be iteratorating over?  In this example
//      we will be iteratorating over the nodes of our Dom, so we will want
//      to iterate over `NodeType`s.
//   b. What is the next element?
//
// We will therefore have the responsibility of keeping track of where we are at
// while iteratorating over our DOM.  We will do this by constructing what is
// oftern called an "iterator interface".  We make one like this:

struct DomMutIterator<'a> {
    data: &'a mut DomNode,
    cursor: usize,
}

// We provide answers for the questions posted above for Rust by implementing an
// `Iterator` for our iterator interface.

impl<'a> Iterator for DomMutIterator<'a> {
    type Item = &'a mut NodeType;
    fn next(&mut self) -> Option<&'a mut NodeType> {

        // This part will require some thought...

        unimplemented!();
    }
}

fn main() {
}

// Note: Rust implementation of creating a mutable iterator uses a `Range` object which, keeps track of the size of the vector.
// this allows for unchecked boundes [see here](https://doc.rust-lang.org/src/collections/up/src/libcollections/btree/map.rs.html#920).
// we should leave this as an exercise to the reader and show them the source to reference. They use the `Range` object to call the
// recurse in the vector [see here](https://doc.rust-lang.org/src/collections/up/src/libcollections/btree/map.rs.html#920).  Perhaps
// this might be a good idea for us as well.
<commit_msg>minor changes<commit_after>#![allow(dead_code)]

// # Mutable Iterators

// ## Part 1

// To start out our introduction to constructing mutable iterators, we will use
// a simple data structure consisting nothing but a vector of integers.  But
// these are our integers, so they are special, which is why we will now call
// them our `SpecialIntegers` type.

struct SpecialIntegers(Vec<u32>);

// To implement an iterator, we essentially need to tell Rust two things:
//   a. What kind of elements will we be iterating over.  When iterating
//      over our structure, we will want our special integers (which are `u32`).
//   b. What is the next element when we are iterating.
//
// We will therefore have the responsibility of keeping track of where we are at
// while iterating.  We will do this by constructing what is
// oftern called an "iterator interface".  To keep track of our iteration, we will
// need our `SpecialIntegers` data structure and a cursor to keep track of where
// we are.

struct SpecialIntegersIterator<'a> {
    data: &'a mut Vec<u32>,
    cursor: usize,
}

// We now need to tell rust how to get the next element in our iterator, which
// we accomplish by implementing the `Iterator` trait on our interator inteface.
// There is a problem that we will run into however.  Our iterator interface
// has a mutable borrow of our `SpecialIntegers` data structure.  So while we
// are holding this mutable borrow, we aren't technically allowed to give a mutable
// reference to elements in our list.  We therefore will be required to use unsafe.
// But before we do so, we should stop and think for a second: is what I am doing
// actually safe?

impl<'a> Iterator for SpecialIntegersIterator<'a> {
    // The type of object we will be iterating over
    type Item = &'a mut u32;
    fn next(&mut self) -> Option<&'a mut u32> {
        if self.cursor < self.data.len() {
            let c = self.cursor;
            self.cursor += 1;
            unimplemented!();  // TODO: Need to figure something out.
        } else {
            None
        }
    }
}

// However, before we will be able to use this iterator interface, unless you
// are planning on creating a brand new `SpecialIntegersIterator` structure
// each you want to use an iterator, we need to tell Rust how to make this structure
// for wehenever we need an iterator.  We do this by implementing the `IntoIterator`
// trait.  Which tells rust how to make an iterator interface by calling
// `into_iter`.

impl<'a> IntoIterator for &'a mut SpecialIntegers {
    type Item = &'a mut u32;
    type IntoIter = SpecialIntegersIterator<'a>;

    fn into_iter(self) -> SpecialIntegersIterator<'a> {
        let &mut SpecialIntegers(ref mut v) = self;
        SpecialIntegersIterator {
            data: v,
            cursor: 0,
        }
    }
}

// ## Part 2

// We will implement a simple tree that could, say, represent a simple DOM
// structure.  You may notice that we don't actually allow for text in our
// simple representation, but this isn't important here, what we really want
// to investigate is how we would implement a mutable iterator using our custom
// data structure.  Our DOM structure will only allow for four different types
// of nodes: Header, Paragraph, Text, and Images.

enum NodeType {
    Header,
    Paragraph,
    Text,
    Image,
}

// Each node with have a list of `children` nodes and a tag for the respective
// DOM element type.

struct DomNode {
    children: Vec<DomNode>,
    node_type: NodeType
}

// To implement an iterator, we essentially just need to tell Rust two things:
//   a. What kind of elements will we be iteratorating over?  In this example
//      we will be iteratorating over the nodes of our Dom, so we will want
//      to iterate over `NodeType`s.
//   b. What is the next element?
//
// We will therefore have the responsibility of keeping track of where we are at
// while iteratorating over our DOM.  We will do this by constructing what is
// oftern called an "iterator interface".  We make one like this:

struct DomMutIterator<'a> {
    data: &'a mut DomNode,
    cursor: usize,
}

// We provide answers for the questions posted above for Rust by implementing an
// `Iterator` for our iterator interface.

impl<'a> Iterator for DomMutIterator<'a> {
    type Item = &'a mut NodeType;
    fn next(&mut self) -> Option<&'a mut NodeType> {

        // This part will require some thought...

        unimplemented!();
    }
}

fn main() {
}

// Note: Rust implementation of creating a mutable iterator uses a `Range` object which, keeps track of the size of the vector.
// this allows for unchecked boundes [see here](https://doc.rust-lang.org/src/collections/up/src/libcollections/btree/map.rs.html#920).
// we should leave this as an exercise to the reader and show them the source to reference. They use the `Range` object to call the
// recurse in the vector [see here](https://doc.rust-lang.org/src/collections/up/src/libcollections/btree/map.rs.html#920).  Perhaps
// this might be a good idea for us as well.
<|endoftext|>


<commit_before>#![allow(clippy::needless_doctest_main)]
//! Client for connecting and communicating with a server listener which speaks SrvProtocol.
//!
//! # RPC Call Example
//!
//! ```rust no_run
//! use habitat_common::types::ListenCtlAddr;
//! use habitat_sup_client::SrvClient;
//! use habitat_sup_protocol as protocols;
//! use futures::stream::StreamExt;
//!
//! #[tokio::main]
//! async fn main() {
//!     let listen_addr = ListenCtlAddr::default();
//!     let secret_key = "seekrit";
//!     let msg = protocols::ctl::SvcGetDefaultCfg::default();
//!     let mut response = SrvClient::request(&listen_addr, secret_key, msg).await.unwrap();
//!     while let Some(message_result) = response.next().await {
//!         let reply = message_result.unwrap();
//!         match reply.message_id() {
//!             "ServiceCfg" => {
//!                 let m = reply.parse::<protocols::types::ServiceCfg>().unwrap();
//!                 println!("{}", m.default.unwrap_or_default());
//!             }
//!             "NetErr" => {
//!                 let m = reply.parse::<protocols::net::NetErr>().unwrap();
//!                 println!("{}", m);
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use habitat_sup_protocol as protocol;
#[macro_use]
extern crate log;
use crate::{common::types::ListenCtlAddr,
            protocol::{codec::*,
                       net::NetErr}};
use futures::{sink::SinkExt,
              stream::{Stream,
                       StreamExt}};
use habitat_common as common;
use std::{error,
          fmt,
          io,
          path::PathBuf,
          time::Duration};
use tokio::{net::TcpStream,
            time};
use tokio_util::codec::Framed;

/// Time to wait in milliseconds for a client connection to timeout.
pub const REQ_TIMEOUT: u64 = 10_000;

/// Error types returned by a [`SrvClient`].
#[derive(Debug)]
pub enum SrvClientError {
    /// Connection refused
    ConnectionRefused,
    /// The remote server unexpectedly closed the connection.
    ConnectionClosed,
    /// Unable to locate a secret key on disk.
    CtlSecretNotFound(PathBuf),
    /// Decoding a message from the remote failed.
    Decode(prost::DecodeError),
    /// An Os level IO error occurred.
    Io(io::Error),
    /// An RPC call to the remote was received but failed.
    NetErr(NetErr),
    /// A parse error from an Invalid Color string
    ParseColor(termcolor::ParseColorError),
}

impl error::Error for SrvClientError {}

impl fmt::Display for SrvClientError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let content = match *self {
            SrvClientError::ConnectionClosed => {
                "The connection was unexpectedly closed.\n\nThis may be because the given \
                 Supervisor is in the middle of an orderly shutdown,\nand is no longer processing \
                 command requests."
                                   .to_string()
            }
            SrvClientError::ConnectionRefused => {
                "Unable to contact the Supervisor.\n\nIf the Supervisor you are contacting is \
                 local, this probably means it is not running. You can run a Supervisor in the \
                 foreground with:\n\nhab sup run\n\nOr try restarting the Supervisor through your \
                 operating system's init process or Windows service."
                                                                     .to_string()
            }
            SrvClientError::CtlSecretNotFound(ref path) => {
                format!("No Supervisor CtlGateway secret set in `cli.toml` or found at {}. Run \
                         `hab setup` or run the Supervisor for the first time before attempting \
                         to command the Supervisor.",
                        path.display())
            }
            SrvClientError::Decode(ref err) => format!("{}", err),
            SrvClientError::Io(ref err) => format!("{}", err),
            SrvClientError::NetErr(ref err) => format!("{}", err),
            SrvClientError::ParseColor(ref err) => format!("{}", err),
        };
        write!(f, "{}", content)
    }
}

impl From<NetErr> for SrvClientError {
    fn from(err: NetErr) -> Self { SrvClientError::NetErr(err) }
}

impl From<io::Error> for SrvClientError {
    fn from(err: io::Error) -> Self {
        match err.kind() {
            io::ErrorKind::ConnectionRefused => SrvClientError::ConnectionRefused,
            _ => SrvClientError::Io(err),
        }
    }
}

impl From<prost::DecodeError> for SrvClientError {
    fn from(err: prost::DecodeError) -> Self { SrvClientError::Decode(err) }
}

impl From<termcolor::ParseColorError> for SrvClientError {
    fn from(err: termcolor::ParseColorError) -> Self { SrvClientError::ParseColor(err) }
}

/// Client for connecting and communicating with a server speaking SrvProtocol.
///
/// See module doc for usage.
pub struct SrvClient;

impl SrvClient {
    /// Connect to the remote server with the given secret_key and make a request.
    ///
    /// Returns a stream of `SrvMessage`'s representing the server response.
    pub async fn request(
        address: &ListenCtlAddr,
        secret_key: &str,
        request: impl Into<SrvMessage> + fmt::Debug)
        -> Result<impl Stream<Item = Result<SrvMessage, io::Error>>, SrvClientError> {
        let socket = TcpStream::connect(address.as_ref()).await?;
        let mut socket = Framed::new(socket, SrvCodec::new());
        let mut current_transaction = SrvTxn::default();

        // Send the handshake message to the server
        let mut handshake = protocol::ctl::Handshake::default();
        handshake.secret_key = Some(String::from(secret_key));
        let mut message = SrvMessage::from(handshake);
        message.set_transaction(current_transaction);
        socket.send(message).await?;

        // Verify the handshake response. There are three kinds of errors we could encounter:
        // 1. The handshake timedout
        // 2. The `socket.next()` call returns `None` indicating the connection was unexpectedly
        // closed by the server
        // 3. Any other socket io error
        let handshake_result =
            time::timeout(Duration::from_millis(REQ_TIMEOUT), socket.next()).await;
        handshake_result.map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "client timed out"))?
                        .ok_or(SrvClientError::ConnectionClosed)??;

        // Send the actual request message
        current_transaction.increment();
        let mut message = request.into();
        message.set_transaction(current_transaction);
        trace!("Sending SrvMessage -> {:?}", message);
        socket.send(message).await?;

        // Return the socket for use as a Stream of responses
        Ok(socket)
    }

    pub fn read_secret_key() -> Result<String, SrvClientError> {
        let mut buf = String::new();
        protocol::read_secret_key(protocol::sup_root(None), &mut buf)
            .map_err(SrvClientError::from)?;
        Ok(buf)
    }
}
<commit_msg>Make ctl client return `NetErr`s<commit_after>#![allow(clippy::needless_doctest_main)]
//! Client for connecting and communicating with a server listener which speaks SrvProtocol.
//!
//! # RPC Call Example
//!
//! ```rust no_run
//! use habitat_common::types::ListenCtlAddr;
//! use habitat_sup_client::SrvClient;
//! use habitat_sup_protocol as protocols;
//! use futures::stream::StreamExt;
//!
//! #[tokio::main]
//! async fn main() {
//!     let listen_addr = ListenCtlAddr::default();
//!     let secret_key = "seekrit";
//!     let msg = protocols::ctl::SvcGetDefaultCfg::default();
//!     let mut response = SrvClient::request(&listen_addr, secret_key, msg).await.unwrap();
//!     while let Some(message_result) = response.next().await {
//!         let reply = message_result.unwrap();
//!         match reply.message_id() {
//!             "ServiceCfg" => {
//!                 let m = reply.parse::<protocols::types::ServiceCfg>().unwrap();
//!                 println!("{}", m.default.unwrap_or_default());
//!             }
//!             "NetErr" => {
//!                 let m = reply.parse::<protocols::net::NetErr>().unwrap();
//!                 println!("{}", m);
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use habitat_sup_protocol as protocol;
#[macro_use]
extern crate log;
use crate::{common::types::ListenCtlAddr,
            protocol::{codec::*,
                       net::NetErr}};
use futures::{sink::SinkExt,
              stream::{Stream,
                       StreamExt}};
use habitat_common as common;
use std::{error,
          fmt,
          io,
          path::PathBuf,
          time::Duration};
use tokio::{net::TcpStream,
            time};
use tokio_util::codec::Framed;

/// Time to wait in milliseconds for a client connection to timeout.
pub const REQ_TIMEOUT: u64 = 10_000;

/// Error types returned by a [`SrvClient`].
#[derive(Debug)]
pub enum SrvClientError {
    /// Connection refused
    ConnectionRefused,
    /// The remote server unexpectedly closed the connection.
    ConnectionClosed,
    /// Unable to locate a secret key on disk.
    CtlSecretNotFound(PathBuf),
    /// Decoding a message from the remote failed.
    Decode(prost::DecodeError),
    /// An Os level IO error occurred.
    Io(io::Error),
    /// An RPC call to the remote was received but failed.
    NetErr(NetErr),
    /// A parse error from an Invalid Color string
    ParseColor(termcolor::ParseColorError),
}

impl error::Error for SrvClientError {}

impl fmt::Display for SrvClientError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let content = match *self {
            SrvClientError::ConnectionClosed => {
                "The connection was unexpectedly closed.\n\nThis may be because the given \
                 Supervisor is in the middle of an orderly shutdown,\nand is no longer processing \
                 command requests."
                                   .to_string()
            }
            SrvClientError::ConnectionRefused => {
                "Unable to contact the Supervisor.\n\nIf the Supervisor you are contacting is \
                 local, this probably means it is not running. You can run a Supervisor in the \
                 foreground with:\n\nhab sup run\n\nOr try restarting the Supervisor through your \
                 operating system's init process or Windows service."
                                                                     .to_string()
            }
            SrvClientError::CtlSecretNotFound(ref path) => {
                format!("No Supervisor CtlGateway secret set in `cli.toml` or found at {}. Run \
                         `hab setup` or run the Supervisor for the first time before attempting \
                         to command the Supervisor.",
                        path.display())
            }
            SrvClientError::Decode(ref err) => format!("{}", err),
            SrvClientError::Io(ref err) => format!("{}", err),
            SrvClientError::NetErr(ref err) => format!("{}", err),
            SrvClientError::ParseColor(ref err) => format!("{}", err),
        };
        write!(f, "{}", content)
    }
}

impl From<NetErr> for SrvClientError {
    fn from(err: NetErr) -> Self { SrvClientError::NetErr(err) }
}

impl From<io::Error> for SrvClientError {
    fn from(err: io::Error) -> Self {
        match err.kind() {
            io::ErrorKind::ConnectionRefused => SrvClientError::ConnectionRefused,
            _ => SrvClientError::Io(err),
        }
    }
}

impl From<prost::DecodeError> for SrvClientError {
    fn from(err: prost::DecodeError) -> Self { SrvClientError::Decode(err) }
}

impl From<termcolor::ParseColorError> for SrvClientError {
    fn from(err: termcolor::ParseColorError) -> Self { SrvClientError::ParseColor(err) }
}

/// Client for connecting and communicating with a server speaking SrvProtocol.
///
/// See module doc for usage.
pub struct SrvClient;

impl SrvClient {
    /// Connect to the remote server with the given secret_key and make a request.
    ///
    /// Returns a stream of `SrvMessage`'s representing the server response.
    pub async fn request(
        address: &ListenCtlAddr,
        secret_key: &str,
        request: impl Into<SrvMessage> + fmt::Debug)
        -> Result<impl Stream<Item = Result<SrvMessage, io::Error>>, SrvClientError> {
        let socket = TcpStream::connect(address.as_ref()).await?;
        let mut socket = Framed::new(socket, SrvCodec::new());
        let mut current_transaction = SrvTxn::default();

        // Send the handshake message to the server
        let mut handshake = protocol::ctl::Handshake::default();
        handshake.secret_key = Some(String::from(secret_key));
        let mut message = SrvMessage::from(handshake);
        message.set_transaction(current_transaction);
        socket.send(message).await?;

        // Verify the handshake response. There are three kinds of errors we could encounter:
        // 1. The handshake timedout
        // 2. The `socket.next()` call returns `None` indicating the connection was unexpectedly
        // closed by the server
        // 3. Any other socket io error
        let handshake_result =
            time::timeout(Duration::from_millis(REQ_TIMEOUT), socket.next()).await;
        let handshake_reply = handshake_result.map_err(|_| {
                                                  io::Error::new(io::ErrorKind::TimedOut,
                                                                 "client timed out")
                                              })?
                                              .ok_or(SrvClientError::ConnectionClosed)??;
        if handshake_reply.message_id() == "NetErr" {
            let m = handshake_reply.parse::<protocol::net::NetErr>()
                                   .map_err(SrvClientError::Decode)?;
            return Err(SrvClientError::from(m));
        }

        // Send the actual request message
        current_transaction.increment();
        let mut message = request.into();
        message.set_transaction(current_transaction);
        trace!("Sending SrvMessage -> {:?}", message);
        socket.send(message).await?;

        // Return the socket for use as a Stream of responses
        Ok(socket)
    }

    pub fn read_secret_key() -> Result<String, SrvClientError> {
        let mut buf = String::new();
        protocol::read_secret_key(protocol::sup_root(None), &mut buf)
            .map_err(SrvClientError::from)?;
        Ok(buf)
    }
}
<|endoftext|>


<commit_before>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}{}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        YELLOW,
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this with one readable if-statement instead?
    if let Some(current_branch_4_realz) = current_branch {
        if current_branch_4_realz == part {
            return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
        }
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // If we find multiple options, pick the one with the lowest number of
    // slashes
    let mut fewest_slashes: Vec<&str> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in candidates {
        if candidate.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = candidate.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse than what we already have, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    if fewest_slashes.is_empty() {
        return None;
    }

    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }
}
<commit_msg>Add a TODO<commit_after>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}{}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        YELLOW,
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this with one readable if-statement instead?
    if let Some(current_branch_4_realz) = current_branch {
        if current_branch_4_realz == part {
            return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
        }
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // If we find multiple options, pick the one with the lowest number of
    // slashes
    let mut fewest_slashes: Vec<&str> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in candidates {
        if candidate.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = candidate.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse than what we already have, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    if fewest_slashes.is_empty() {
        return None;
    }

    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }

    FIXME: Add a test for adding blue background to a commit line with some tags and branches
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;

use futures::future::{Executor, Future};

use body::Payload;
use proto::h2::server::H2Stream;
use server::conn::spawn_all::{NewSvcTask, Watcher};
use service::Service;

pub trait H2Exec<F, B: Payload>: Clone {
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()>;
}

pub trait NewSvcExec<I, N, S: Service, E, W: Watcher<I, S, E>>: Clone {
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()>;
}

// Either the user provides an executor for background tasks, or we use
// `tokio::spawn`.
#[derive(Clone)]
pub enum Exec {
    Default,
    Executor(Arc<Executor<Box<Future<Item=(), Error=()> + Send>> + Send + Sync>),
}

// ===== impl Exec =====

impl Exec {
    pub(crate) fn execute<F>(&self, fut: F) -> ::Result<()>
    where
        F: Future<Item=(), Error=()> + Send + 'static,
    {
        match *self {
            Exec::Default => {
                #[cfg(feature = "runtime")]
                {
                    use ::tokio_executor::Executor;
                    ::tokio_executor::DefaultExecutor::current()
                        .spawn(Box::new(fut))
                        .map_err(|err| {
                            warn!("executor error: {:?}", err);
                            ::Error::new_execute(TokioSpawnError)
                        })
                }
                #[cfg(not(feature = "runtime"))]
                {
                    // If no runtime, we need an executor!
                    panic!("executor must be set")
                }
            },
            Exec::Executor(ref e) => {
                e.execute(Box::new(fut))
                    .map_err(|err| {
                        warn!("executor error: {:?}", err.kind());
                        ::Error::new_execute("custom executor failed")
                    })
            },
        }
    }
}

impl fmt::Debug for Exec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Exec")
            .finish()
    }
}

impl<F, B> H2Exec<F, B> for Exec
where
    H2Stream<F, B>: Future<Item=(), Error=()> + Send + 'static,
    B: Payload,
{
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
        self.execute(fut)
    }
}

impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
where
    NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()> + Send + 'static,
    S: Service,
    W: Watcher<I, S, E>,
{
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
        self.execute(fut)
    }
}

// ==== impl Executor =====

impl<E, F, B> H2Exec<F, B> for E
where
    E: Executor<H2Stream<F, B>> + Clone,
    H2Stream<F, B>: Future<Item=(), Error=()>,
    B: Payload,
{
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
        self.execute(fut)
            .map_err(|err| {
                warn!("executor error: {:?}", err.kind());
                ::Error::new_execute("custom executor failed")
            })
    }
}

impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
where
    E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
    NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()>,
    S: Service,
    W: Watcher<I, S, E>,
{
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
        self.execute(fut)
            .map_err(|err| {
                warn!("executor error: {:?}", err.kind());
                ::Error::new_execute("custom executor failed")
            })
    }
}

// ===== StdError impls =====

struct TokioSpawnError;

impl fmt::Debug for TokioSpawnError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt("tokio::spawn failed (is a tokio runtime running this future?)", f)
    }
}

impl fmt::Display for TokioSpawnError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt("tokio::spawn failed (is a tokio runtime running this future?)", f)
    }
}

impl StdError for TokioSpawnError {
    fn description(&self) -> &str {
        "tokio::spawn failed"
    }
}

<commit_msg>refactor(exec): fix unused warning when runtime feature is disabled<commit_after>use std::fmt;
use std::sync::Arc;

use futures::future::{Executor, Future};

use body::Payload;
use proto::h2::server::H2Stream;
use server::conn::spawn_all::{NewSvcTask, Watcher};
use service::Service;

pub trait H2Exec<F, B: Payload>: Clone {
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()>;
}

pub trait NewSvcExec<I, N, S: Service, E, W: Watcher<I, S, E>>: Clone {
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()>;
}

// Either the user provides an executor for background tasks, or we use
// `tokio::spawn`.
#[derive(Clone)]
pub enum Exec {
    Default,
    Executor(Arc<Executor<Box<Future<Item=(), Error=()> + Send>> + Send + Sync>),
}

// ===== impl Exec =====

impl Exec {
    pub(crate) fn execute<F>(&self, fut: F) -> ::Result<()>
    where
        F: Future<Item=(), Error=()> + Send + 'static,
    {
        match *self {
            Exec::Default => {
                #[cfg(feature = "runtime")]
                {
                    use std::error::Error as StdError;
                    use ::tokio_executor::Executor;

                    struct TokioSpawnError;

                    impl fmt::Debug for TokioSpawnError {
                        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                            fmt::Debug::fmt("tokio::spawn failed (is a tokio runtime running this future?)", f)
                        }
                    }

                    impl fmt::Display for TokioSpawnError {
                        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                            fmt::Display::fmt("tokio::spawn failed (is a tokio runtime running this future?)", f)
                        }
                    }

                    impl StdError for TokioSpawnError {
                        fn description(&self) -> &str {
                            "tokio::spawn failed"
                        }
                    }

                    ::tokio_executor::DefaultExecutor::current()
                        .spawn(Box::new(fut))
                        .map_err(|err| {
                            warn!("executor error: {:?}", err);
                            ::Error::new_execute(TokioSpawnError)
                        })
                }
                #[cfg(not(feature = "runtime"))]
                {
                    // If no runtime, we need an executor!
                    panic!("executor must be set")
                }
            },
            Exec::Executor(ref e) => {
                e.execute(Box::new(fut))
                    .map_err(|err| {
                        warn!("executor error: {:?}", err.kind());
                        ::Error::new_execute("custom executor failed")
                    })
            },
        }
    }
}

impl fmt::Debug for Exec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Exec")
            .finish()
    }
}

impl<F, B> H2Exec<F, B> for Exec
where
    H2Stream<F, B>: Future<Item=(), Error=()> + Send + 'static,
    B: Payload,
{
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
        self.execute(fut)
    }
}

impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for Exec
where
    NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()> + Send + 'static,
    S: Service,
    W: Watcher<I, S, E>,
{
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
        self.execute(fut)
    }
}

// ==== impl Executor =====

impl<E, F, B> H2Exec<F, B> for E
where
    E: Executor<H2Stream<F, B>> + Clone,
    H2Stream<F, B>: Future<Item=(), Error=()>,
    B: Payload,
{
    fn execute_h2stream(&self, fut: H2Stream<F, B>) -> ::Result<()> {
        self.execute(fut)
            .map_err(|err| {
                warn!("executor error: {:?}", err.kind());
                ::Error::new_execute("custom executor failed")
            })
    }
}

impl<I, N, S, E, W> NewSvcExec<I, N, S, E, W> for E
where
    E: Executor<NewSvcTask<I, N, S, E, W>> + Clone,
    NewSvcTask<I, N, S, E, W>: Future<Item=(), Error=()>,
    S: Service,
    W: Watcher<I, S, E>,
{
    fn execute_new_svc(&self, fut: NewSvcTask<I, N, S, E, W>) -> ::Result<()> {
        self.execute(fut)
            .map_err(|err| {
                warn!("executor error: {:?}", err.kind());
                ::Error::new_execute("custom executor failed")
            })
    }
}

// ===== StdError impls =====

<|endoftext|>


<commit_before>use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::TyCtxt;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;

pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
    assert_eq!(crate_num, LOCAL_CRATE);
    let krate = tcx.hir().krate();
    krate.visit_all_item_likes(&mut InherentOverlapChecker { tcx });
}

struct InherentOverlapChecker<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl InherentOverlapChecker<'tcx> {
    fn check_for_common_items_in_impls(
        &self,
        impl1: DefId,
        impl2: DefId,
        overlap: traits::OverlapResult<'_>,
    ) {
        let name_and_namespace = |def_id| {
            let item = self.tcx.associated_item(def_id);
            (item.ident.modern(), Namespace::from(item.kind))
        };

        let impl_items1 = self.tcx.associated_item_def_ids(impl1);
        let impl_items2 = self.tcx.associated_item_def_ids(impl2);

        for &item1 in &impl_items1[..] {
            let (name, namespace) = name_and_namespace(item1);

            for &item2 in &impl_items2[..] {
                if (name, namespace) == name_and_namespace(item2) {
                    let mut err = struct_span_err!(
                        self.tcx.sess,
                        self.tcx.span_of_impl(item1).unwrap(),
                        E0592,
                        "duplicate definitions with name `{}`",
                        name
                    );
                    err.span_label(
                        self.tcx.span_of_impl(item1).unwrap(),
                        format!("duplicate definitions for `{}`", name),
                    );
                    err.span_label(
                        self.tcx.span_of_impl(item2).unwrap(),
                        format!("other definition for `{}`", name),
                    );

                    for cause in &overlap.intercrate_ambiguity_causes {
                        cause.add_intercrate_ambiguity_hint(&mut err);
                    }

                    if overlap.involves_placeholder {
                        traits::add_placeholder_note(&mut err);
                    }

                    err.emit();
                }
            }
        }
    }

    fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
        let impls = self.tcx.inherent_impls(ty_def_id);

        for (i, &impl1_def_id) in impls.iter().enumerate() {
            for &impl2_def_id in &impls[(i + 1)..] {
                traits::overlapping_impls(
                    self.tcx,
                    impl1_def_id,
                    impl2_def_id,
                    IntercrateMode::Issue43355,
                    // We go ahead and just skip the leak check for
                    // inherent impls without warning.
                    SkipLeakCheck::Yes,
                    |overlap| {
                        self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
                        false
                    },
                    || true,
                );
            }
        }
    }
}

impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
    fn visit_item(&mut self, item: &'v hir::Item<'v>) {
        match item.kind {
            hir::ItemKind::Enum(..)
            | hir::ItemKind::Struct(..)
            | hir::ItemKind::Trait(..)
            | hir::ItemKind::Union(..) => {
                let type_def_id = self.tcx.hir().local_def_id(item.hir_id);
                self.check_for_overlapping_inherent_impls(type_def_id);
            }
            _ => {}
        }
    }

    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'v>) {}

    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'v>) {}
}
<commit_msg>Use `associated_items` query in impl overlap check<commit_after>use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::TyCtxt;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;

pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
    assert_eq!(crate_num, LOCAL_CRATE);
    let krate = tcx.hir().krate();
    krate.visit_all_item_likes(&mut InherentOverlapChecker { tcx });
}

struct InherentOverlapChecker<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl InherentOverlapChecker<'tcx> {
    fn check_for_common_items_in_impls(
        &self,
        impl1: DefId,
        impl2: DefId,
        overlap: traits::OverlapResult<'_>,
    ) {
        let name_and_namespace =
            |assoc: &AssocItem| (assoc.ident.modern(), Namespace::from(assoc.kind));

        let impl_items1 = self.tcx.associated_items(impl1);
        let impl_items2 = self.tcx.associated_items(impl2);

        for item1 in &impl_items1[..] {
            let (name, namespace) = name_and_namespace(item1);

            for item2 in &impl_items2[..] {
                if (name, namespace) == name_and_namespace(item2) {
                    let mut err = struct_span_err!(
                        self.tcx.sess,
                        self.tcx.span_of_impl(item1.def_id).unwrap(),
                        E0592,
                        "duplicate definitions with name `{}`",
                        name
                    );
                    err.span_label(
                        self.tcx.span_of_impl(item1.def_id).unwrap(),
                        format!("duplicate definitions for `{}`", name),
                    );
                    err.span_label(
                        self.tcx.span_of_impl(item2.def_id).unwrap(),
                        format!("other definition for `{}`", name),
                    );

                    for cause in &overlap.intercrate_ambiguity_causes {
                        cause.add_intercrate_ambiguity_hint(&mut err);
                    }

                    if overlap.involves_placeholder {
                        traits::add_placeholder_note(&mut err);
                    }

                    err.emit();
                }
            }
        }
    }

    fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
        let impls = self.tcx.inherent_impls(ty_def_id);

        for (i, &impl1_def_id) in impls.iter().enumerate() {
            for &impl2_def_id in &impls[(i + 1)..] {
                traits::overlapping_impls(
                    self.tcx,
                    impl1_def_id,
                    impl2_def_id,
                    IntercrateMode::Issue43355,
                    // We go ahead and just skip the leak check for
                    // inherent impls without warning.
                    SkipLeakCheck::Yes,
                    |overlap| {
                        self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
                        false
                    },
                    || true,
                );
            }
        }
    }
}

impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
    fn visit_item(&mut self, item: &'v hir::Item<'v>) {
        match item.kind {
            hir::ItemKind::Enum(..)
            | hir::ItemKind::Struct(..)
            | hir::ItemKind::Trait(..)
            | hir::ItemKind::Union(..) => {
                let type_def_id = self.tcx.hir().local_def_id(item.hir_id);
                self.check_for_overlapping_inherent_impls(type_def_id);
            }
            _ => {}
        }
    }

    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'v>) {}

    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'v>) {}
}
<|endoftext|>


<commit_before>use super::*;
use wtools::error::BasicError;
use wca::command::Command;
use wca::instruction::Instruction;

//

fn commands_form() -> std::collections::HashMap< String, Command >
{
  let help_command : Command = wca::CommandOptions::default()
  .hint( "Get help." )
  .long_hint( "Get help for command [command]" )
  .phrase( ".help" )
  .routine( &| _i : &Instruction | { println!( "this is help" ); Ok( () ) } )
  .form();
  let list_command : Command = wca::CommandOptions::default()
  .hint( "Get list." )
  .long_hint( "Get list of" )
  .phrase( ".list" )
  .subject_hint( "some subject" )
  .routine( &| _i : &Instruction | { println!( "this is list" ); Ok( () ) } )
  .form();
  let err_command : Command = wca::CommandOptions::default()
  .hint( "Error." )
  .long_hint( "Throw error" )
  .phrase( ".error" )
  .routine( &| _i : &Instruction | { Err( BasicError::new( "err" ) ) } )
  .form();

  let commands : std::collections::HashMap< String, Command > = std::collections::HashMap::from
  ([
    ( ".help".to_string(), help_command ),
    ( ".list".to_string(), list_command ),
    ( ".error".to_string(), err_command ),
  ]);
  commands
}

tests_impls!
{
  fn basic()
  {
    let ca = wca::commands_aggregator()
    .form();
    a_id!( ca.base_path, None );
    a_id!( ca.command_prefix, "".to_string() );
    a_id!( ca.delimeter, vec![ ".".to_string(), " ".to_string() ] );
    a_id!( ca.command_explicit_delimeter, ";".to_string() );
    a_id!( ca.command_implicit_delimeter, " ".to_string() );
    a_id!( ca.commands_explicit_delimiting, true );
    a_id!( ca.commands_implicit_delimiting, false );
    a_id!( ca.properties_map_parsing, false );
    a_id!( ca.several_values, true );
    a_id!( ca.with_help, true );
    a_id!( ca.changing_exit_code, true );
    a_id!( ca.commands, std::collections::HashMap::new() );
  }

  fn instruction_perform_basic()
  {
    /* no commands in aggregator */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }
}

//

tests_index!
{
  basic,
  instruction_perform_basic,
}

<commit_msg>Write test routine `instructions_parse_basic`<commit_after>use super::*;
use wtools::error::BasicError;
use wca::command::Command;
use wca::instruction::Instruction;
use wca::string::parse_request::OpType::Primitive;

//

fn commands_form() -> std::collections::HashMap< String, Command >
{
  let help_command : Command = wca::CommandOptions::default()
  .hint( "Get help." )
  .long_hint( "Get help for command [command]" )
  .phrase( ".help" )
  .routine( &| _i : &Instruction | { println!( "this is help" ); Ok( () ) } )
  .form();
  let list_command : Command = wca::CommandOptions::default()
  .hint( "Get list." )
  .long_hint( "Get list of" )
  .phrase( ".list" )
  .subject_hint( "some subject" )
  .routine( &| _i : &Instruction | { println!( "this is list" ); Ok( () ) } )
  .form();
  let err_command : Command = wca::CommandOptions::default()
  .hint( "Error." )
  .long_hint( "Throw error" )
  .phrase( ".error" )
  .routine( &| _i : &Instruction | { Err( BasicError::new( "err" ) ) } )
  .form();

  let commands : std::collections::HashMap< String, Command > = std::collections::HashMap::from
  ([
    ( ".help".to_string(), help_command ),
    ( ".list".to_string(), list_command ),
    ( ".error".to_string(), err_command ),
  ]);
  commands
}

tests_impls!
{
  fn basic()
  {
    let ca = wca::commands_aggregator()
    .form();
    a_id!( ca.base_path, None );
    a_id!( ca.command_prefix, "".to_string() );
    a_id!( ca.delimeter, vec![ ".".to_string(), " ".to_string() ] );
    a_id!( ca.command_explicit_delimeter, ";".to_string() );
    a_id!( ca.command_implicit_delimeter, " ".to_string() );
    a_id!( ca.commands_explicit_delimiting, true );
    a_id!( ca.commands_implicit_delimiting, false );
    a_id!( ca.properties_map_parsing, false );
    a_id!( ca.several_values, true );
    a_id!( ca.with_help, true );
    a_id!( ca.changing_exit_code, true );
    a_id!( ca.commands, std::collections::HashMap::new() );
  }

  fn instruction_perform_basic()
  {
    /* no commands in aggregator */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Ok */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".help" );
    a_id!( got, Ok( () ) );

    /* command returns Err */
    let ca = wca::commands_aggregator()
    .changing_exit_code( false )
    .commands().replace( commands_form() ).end()
    .form();
    let got = ca.instruction_perform( ".error" );
    a_id!( got, Err( BasicError::new( "err" ) ) );
  }

  //

  fn instructions_parse_basic()
  {
    let ca = wca::commands_aggregator()
    .form();

    /* */

    /* empty program */
    let got = ca.instructions_parse( "" );
    a_id!( got, vec![] );

    /* */

    /* single command without subject and map */
    let got = ca.instructions_parse( ".help" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject */
    let got = ca.instructions_parse( ".help command" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction ] );

    /* single command with subject and map */
    let got = ca.instructions_parse( ".help command v:3" );
    let instruction = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction ] );

    /* */

    /* two commands without subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help ; .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, explicit delimeter */
    let got = ca.instructions_parse( ".help command ; .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, explicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 ; .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* */

    /* two commands without subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help .version" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject, implicit delimeter */
    let got = ca.instructions_parse( ".help command .version delta" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::new(),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::new(),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );

    /* two commands with subject and map, implicit delimeter */
    let got = ca.instructions_parse( ".help command v:3 .version delta n:5" );
    let instruction1 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".help".to_string(),
      subject : "command".to_string(),
      properties_map : HashMap::from([ ( "v".to_string(), Primitive( "3".to_string() ) ) ]),
    };
    let instruction2 = wca::instruction::Instruction
    {
      err : None,
      command_name : ".version".to_string(),
      subject : "delta".to_string(),
      properties_map : HashMap::from([ ( "n".to_string(), Primitive( "5".to_string() ) ) ]),
    };
    a_id!( got, vec![ instruction1, instruction2 ] );
  }
}

//

tests_index!
{
  basic,
  instruction_perform_basic,
  instructions_parse_basic,
}

<|endoftext|>


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

#![feature(core)]
#![feature(libc)]
extern crate libc;
extern crate docopt;

pub mod ffipredict;
pub mod predict;
pub mod usage;
<commit_msg>remove feature core and libc and add test to lib.rs<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

extern crate libc;
extern crate docopt;

pub mod ffipredict;
pub mod predict;
pub mod usage;

#[test]
fn test_update() {
    let tle: predict::Tle = predict::Tle{
        name: "ESTCUBE 1".to_string(),
        line1: "1 39161U 13021C   15091.47675532  .00001890  00000-0  31643-3 0  9990".to_string(),
        line2: "2 39161  98.0727 175.0786 0009451 192.0216 168.0788 14.70951130101965".to_string()
    };

    let location: predict::Location = predict::Location{lat_deg:58.64560, lon_deg: 23.15163, alt_m: 8};
    let mut predict: predict::Predict = predict::Predict::new(tle, location);

    predict.update(Some(0.0));
    println!("az         : {:.*}°", 2, predict.sat.az_deg);
    println!("el         : {:.*}°", 2, predict.sat.el_deg);
    println!("range      : {:.*} km", 0, predict.sat.range_km);
    println!("range rate : {:.*} km/sec\n", 3, predict.sat.range_rate_km_sec);
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate toml;
extern crate semver;
extern crate mktemp;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};
use std::path::{Path, PathBuf};
use std::process::Command;

use hyper::client::Client;
use mktemp::Temp;
use semver::Version;

type Crate = (String, String);

fn get_top_crates() -> serde_json::Value {
    let client = Client::new();

    let res = client.get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads").send().expect("Could not fetch top crates");
    assert_eq!(res.status, hyper::Ok);

    serde_json::from_reader(res).expect("Invalid JSON")
}

fn top_crates_to_crates(summary: serde_json::Value) -> Vec<Crate> {
    let summary = summary.as_object().expect("Not object");
    let most_downloaded = summary.get("crates").expect("Missing most_downloaded");
    let most_downloaded = most_downloaded.as_array().expect("crates not an array");

    most_downloaded.iter().map(|krate| {
        let krate = krate.as_object().expect("crate not an object");
        let id = krate.get("id").expect("missing id").as_str().expect("id not string");
        let version = krate.get("max_version").expect("missing version").as_str().expect("version not string");

        (id.into(), version.into())
    }).collect()
}

fn crates_to_toml(crates: Vec<Crate>) -> toml::Value {
    use toml::Value;

    let mut package = toml::Table::new();
    package.insert("authors".into(), Value::Array(vec![Value::String("The Rust Playground".into())]));
    package.insert("name".into(), Value::String("playground".into()));
    package.insert("version".into(), Value::String("0.0.1".into()));

    let mut dependencies = toml::Table::new();
    for (id, version) in crates {
        dependencies.insert(id, Value::String(version));
    }

    let mut result = toml::Table::new();
    result.insert("package".into(), Value::Table(package));
    result.insert("dependencies".into(), Value::Table(dependencies));

    Value::Table(result)
}

fn write_cargo_toml(dir: &Path, cargo_toml: toml::Value) -> PathBuf {
    let toml_file = dir.join("Cargo.toml");

    let f = File::create(&toml_file).expect("Unable to create Cargo.toml");
    let mut f = BufWriter::new(f);

    f.write_all(toml::encode_str(&cargo_toml).as_bytes()).expect("Couldn't write Cargo.toml");

    toml_file
}

fn resolve_dependencies(dir: &Path, cargo_toml: toml::Value) -> PathBuf {
    let x = Command::new("cargo")
        .args(&["new", "--bin", "dependencies"])
        .current_dir(dir)
        .status().expect("Couldn't create scratch project");
    assert!(x.success(), "Didn't run cargo new");

    let project_dir = dir.join("dependencies");
    write_cargo_toml(&project_dir, cargo_toml);

    let x = Command::new("cargo")
        .args(&["fetch"])
        .current_dir(&project_dir)
        .status().expect("Couldn't resolve dependencies");
    assert!(x.success(), "Didn't run cargo fetch");

    project_dir.join("Cargo.lock")
}

fn get_lockfile(lockfile_path: &Path) -> toml::Value {
    let f = File::open(lockfile_path).expect("Couldn't open the lockfile");
    let mut f = BufReader::new(f);

    let mut s = String::new();
    f.read_to_string(&mut s).expect("Couldn't read the lockfile");

    toml::decode_str(&s).expect("Unable to parse lockfile")
}

fn lockfile_to_crates(lockfile: toml::Value) -> Vec<Crate> {
    let packages = lockfile.lookup("package").expect("Couldn't find packages");
    let packages = packages.as_slice().expect("packages not an array");

    packages.iter().map(|package| {
        let package = package.as_table().expect("not an object");
        let name = package.get("name").expect("missing name").as_str().expect("name not string");
        let version = package.get("version").expect("missing version").as_str().expect("version not string");

        (name.into(), version.into())
    }).collect()
}

fn unique_latest_crates(crates: Vec<Crate>) -> Vec<Crate> {
    let mut uniqs = HashMap::new();
    for (name, version) in crates {
        let version = Version::parse(&version).expect("Invalid version");

        match uniqs.entry(name) {
            Entry::Vacant(entry) => {
                entry.insert(version);
            }
            Entry::Occupied(mut entry) => {
                if &version > entry.get() {
                    entry.insert(version);
                }
            }
        }
    }

    uniqs.into_iter().map(|(n, v)| (n, v.to_string())).collect()
}

static BLACKLIST: &'static [&'static str] = &[
    "libressl-pnacl-sys", // Fails to build
    "pnacl-build-helper", // Fails to build
    "aster", // Not supported on stable
    "quasi", // Not supported on stable
    "quasi_codegen", // Not supported on stable
    "quasi_macros", // Not supported on stable
];

fn remove_blacklisted_crates(crates: Vec<Crate>) -> Vec<Crate> {
    crates.into_iter().filter(|&(ref name, _)| !BLACKLIST.contains(&&**name)).collect()
}

fn main() {
    let scratch = Temp::new_dir().expect("unable to make scratch dir");

    let top_crates = get_top_crates();
    let crates = top_crates_to_crates(top_crates);

    let cargo_toml_rev1 = crates_to_toml(crates);
    let lockfile_path = resolve_dependencies(scratch.as_ref(), cargo_toml_rev1);

    let lockfile = get_lockfile(&lockfile_path);
    let crates_rev2 = lockfile_to_crates(lockfile);

    let crates_rev2 = unique_latest_crates(crates_rev2);
    let crates_rev2 = remove_blacklisted_crates(crates_rev2);
    let cargo_toml_rev2 = crates_to_toml(crates_rev2);

    let result_file = write_cargo_toml(scratch.as_ref(), cargo_toml_rev2);
    fs::rename(result_file, "result.Cargo.toml").expect("Couldn't rename");
}
<commit_msg>Blacklist serde_macros<commit_after>extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate toml;
extern crate semver;
extern crate mktemp;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};
use std::path::{Path, PathBuf};
use std::process::Command;

use hyper::client::Client;
use mktemp::Temp;
use semver::Version;

type Crate = (String, String);

fn get_top_crates() -> serde_json::Value {
    let client = Client::new();

    let res = client.get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads").send().expect("Could not fetch top crates");
    assert_eq!(res.status, hyper::Ok);

    serde_json::from_reader(res).expect("Invalid JSON")
}

fn top_crates_to_crates(summary: serde_json::Value) -> Vec<Crate> {
    let summary = summary.as_object().expect("Not object");
    let most_downloaded = summary.get("crates").expect("Missing most_downloaded");
    let most_downloaded = most_downloaded.as_array().expect("crates not an array");

    most_downloaded.iter().map(|krate| {
        let krate = krate.as_object().expect("crate not an object");
        let id = krate.get("id").expect("missing id").as_str().expect("id not string");
        let version = krate.get("max_version").expect("missing version").as_str().expect("version not string");

        (id.into(), version.into())
    }).collect()
}

fn crates_to_toml(crates: Vec<Crate>) -> toml::Value {
    use toml::Value;

    let mut package = toml::Table::new();
    package.insert("authors".into(), Value::Array(vec![Value::String("The Rust Playground".into())]));
    package.insert("name".into(), Value::String("playground".into()));
    package.insert("version".into(), Value::String("0.0.1".into()));

    let mut dependencies = toml::Table::new();
    for (id, version) in crates {
        dependencies.insert(id, Value::String(version));
    }

    let mut result = toml::Table::new();
    result.insert("package".into(), Value::Table(package));
    result.insert("dependencies".into(), Value::Table(dependencies));

    Value::Table(result)
}

fn write_cargo_toml(dir: &Path, cargo_toml: toml::Value) -> PathBuf {
    let toml_file = dir.join("Cargo.toml");

    let f = File::create(&toml_file).expect("Unable to create Cargo.toml");
    let mut f = BufWriter::new(f);

    f.write_all(toml::encode_str(&cargo_toml).as_bytes()).expect("Couldn't write Cargo.toml");

    toml_file
}

fn resolve_dependencies(dir: &Path, cargo_toml: toml::Value) -> PathBuf {
    let x = Command::new("cargo")
        .args(&["new", "--bin", "dependencies"])
        .current_dir(dir)
        .status().expect("Couldn't create scratch project");
    assert!(x.success(), "Didn't run cargo new");

    let project_dir = dir.join("dependencies");
    write_cargo_toml(&project_dir, cargo_toml);

    let x = Command::new("cargo")
        .args(&["fetch"])
        .current_dir(&project_dir)
        .status().expect("Couldn't resolve dependencies");
    assert!(x.success(), "Didn't run cargo fetch");

    project_dir.join("Cargo.lock")
}

fn get_lockfile(lockfile_path: &Path) -> toml::Value {
    let f = File::open(lockfile_path).expect("Couldn't open the lockfile");
    let mut f = BufReader::new(f);

    let mut s = String::new();
    f.read_to_string(&mut s).expect("Couldn't read the lockfile");

    toml::decode_str(&s).expect("Unable to parse lockfile")
}

fn lockfile_to_crates(lockfile: toml::Value) -> Vec<Crate> {
    let packages = lockfile.lookup("package").expect("Couldn't find packages");
    let packages = packages.as_slice().expect("packages not an array");

    packages.iter().map(|package| {
        let package = package.as_table().expect("not an object");
        let name = package.get("name").expect("missing name").as_str().expect("name not string");
        let version = package.get("version").expect("missing version").as_str().expect("version not string");

        (name.into(), version.into())
    }).collect()
}

fn unique_latest_crates(crates: Vec<Crate>) -> Vec<Crate> {
    let mut uniqs = HashMap::new();
    for (name, version) in crates {
        let version = Version::parse(&version).expect("Invalid version");

        match uniqs.entry(name) {
            Entry::Vacant(entry) => {
                entry.insert(version);
            }
            Entry::Occupied(mut entry) => {
                if &version > entry.get() {
                    entry.insert(version);
                }
            }
        }
    }

    uniqs.into_iter().map(|(n, v)| (n, v.to_string())).collect()
}

static BLACKLIST: &'static [&'static str] = &[
    "libressl-pnacl-sys", // Fails to build
    "pnacl-build-helper", // Fails to build
    "aster", // Not supported on stable
    "quasi", // Not supported on stable
    "quasi_codegen", // Not supported on stable
    "quasi_macros", // Not supported on stable
    "serde_macros", // Apparently deleted
];

fn remove_blacklisted_crates(crates: Vec<Crate>) -> Vec<Crate> {
    crates.into_iter().filter(|&(ref name, _)| !BLACKLIST.contains(&&**name)).collect()
}

fn main() {
    let scratch = Temp::new_dir().expect("unable to make scratch dir");

    let top_crates = get_top_crates();
    let crates = top_crates_to_crates(top_crates);
    let crates = remove_blacklisted_crates(crates);

    let cargo_toml_rev1 = crates_to_toml(crates);
    let lockfile_path = resolve_dependencies(scratch.as_ref(), cargo_toml_rev1);

    let lockfile = get_lockfile(&lockfile_path);
    let crates_rev2 = lockfile_to_crates(lockfile);

    let crates_rev2 = unique_latest_crates(crates_rev2);
    let crates_rev2 = remove_blacklisted_crates(crates_rev2);
    let cargo_toml_rev2 = crates_to_toml(crates_rev2);

    let result_file = write_cargo_toml(scratch.as_ref(), cargo_toml_rev2);
    fs::rename(result_file, "result.Cargo.toml").expect("Couldn't rename");
}
<|endoftext|>