<commit_before>use crate::{
    command_line::{CommandLine, Entry},
    model::{Model, Msg},
    schema::{
        default_renderer, Field, Kind, Schema, ValidatorContext, RUST_FUNCTION_CALL, SCHEMA, *,
    },
    types::{parent, Hash, Mode, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
    // Memoize callbacks?
    ondelete: Callback<()>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub allowed_kinds: &'static [&'static str],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        let updatemodel = ctx.props().updatemodel.clone();
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
            ondelete: Callback::from(move |()| updatemodel.emit(Msg::Parent)),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
        let props = ctx.props();
        let model = &props.model;
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected && model.mode == Mode::Edit {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let model = &props.model;
        let hash = props.hash.clone().unwrap_or_default();
        let node = props
            .model
            .node_store
            .lookup_hash(&hash)
            .unwrap_or(&default_node);
        if props.hash.is_none() {}
        let path = props.path.clone();
        let cursor = model.cursor.clone();
        let _oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if props.hash.is_none() || node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .allowed_kinds
                .iter()
                .map(|kind_id| Entry {
                    label: SCHEMA
                        .get_kind(kind_id)
                        .map(|k| k.name.clone())
                        .unwrap_or("INVALID")
                        .to_string(),
                    description: "".to_string(),
                    action: Msg::ReplaceNode(
                        path.clone(),
                        Node {
                            kind: kind_id.to_string(),
                            value: "".to_string(),
                            links: BTreeMap::new(),
                        },
                        false,
                    ),
                    valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                })
                .collect();
            let onenter = {
                let _path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            let placeholder = if props.hash.is_none() {
                "***".to_string()
            } else {
                "".to_string()
            };
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                placeholder={ placeholder }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected && model.mode == Mode::Edit }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.and_then(|k| k.renderer).unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if model.mode == Mode::Edit && selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let mut all_entries = vec![];
                        let mut field_entries = k
                            .get_fields()
                            .iter()
                            .map(|(field_id, field)| Entry {
                                label: field.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), **field_id),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect();
                        // TODO: macros
                        let mut macro_entries = vec![
                            Entry {
                                label: "delete".to_string(),
                                description: "".to_string(),
                                action: Msg::DeleteItem,
                                valid_classes: vec![],
                            },
                            // TODO: should apply to literals too.
                            Entry {
                                label: "call".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_FUNCTION_CALL.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "array".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_ARRAY_TYPE.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "move up".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    parent(&path).to_vec(),
                                    node.clone(),
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                        ];
                        all_entries.append(&mut field_entries);
                        all_entries.append(&mut macro_entries);
                        all_entries
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let _onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {
                    <div class="pl-3 absolute bg-white">
                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ self.ondelete.clone() }
                            enabled=true
                        />
                    </div>
                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path;
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {
            <div
              class={ classes.join(" ") }
              tabindex="0"
              onclick={ onclick }
            >
              { inner }
            </div>
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = false;
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        if let Some(old_props) = &self.old_props {
            log::debug!("same path: {:?}", old_props.path == ctx.props().path);
            log::debug!("same model: {:?}", old_props.model == ctx.props().model);
            log::debug!("same hash: {:?}", old_props.hash == ctx.props().hash);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == ctx.props().updatemodel
            );
            return old_props.path != ctx.props().path
                || old_props.model != ctx.props().model
                || old_props.hash != ctx.props().hash;
        }
        self.old_props = Some(ctx.props().clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        if let Some(i) = input_node.cast::<HtmlInputElement>() {
            i.focus().unwrap();
        }
    }
}
<commit_msg>Fix string clone<commit_after>use crate::{
    command_line::{CommandLine, Entry},
    model::{Model, Msg},
    schema::{
        default_renderer, Field, Kind, Schema, ValidatorContext, RUST_FUNCTION_CALL, SCHEMA, *,
    },
    types::{parent, Hash, Mode, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
    // Memoize callbacks?
    ondelete: Callback<()>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub allowed_kinds: &'static [&'static str],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        let updatemodel = ctx.props().updatemodel.clone();
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
            ondelete: Callback::from(move |()| updatemodel.emit(Msg::Parent)),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
        let props = ctx.props();
        let model = &props.model;
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected && model.mode == Mode::Edit {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let model = &props.model;
        let hash = props.hash.clone().unwrap_or_default();
        let node = props
            .model
            .node_store
            .lookup_hash(&hash)
            .unwrap_or(&default_node);
        if props.hash.is_none() {}
        let path = props.path.clone();
        let cursor = model.cursor.clone();
        let _oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if props.hash.is_none() || node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .allowed_kinds
                .iter()
                .map(|kind_id| Entry {
                    label: SCHEMA
                        .get_kind(kind_id)
                        .map(|k| &(*k.name))
                        .unwrap_or("INVALID")
                        .to_string(),
                    description: "".to_string(),
                    action: Msg::ReplaceNode(
                        path.clone(),
                        Node {
                            kind: kind_id.to_string(),
                            value: "".to_string(),
                            links: BTreeMap::new(),
                        },
                        false,
                    ),
                    valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                })
                .collect();
            let onenter = {
                let _path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            let placeholder = if props.hash.is_none() {
                "***".to_string()
            } else {
                "".to_string()
            };
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                placeholder={ placeholder }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected && model.mode == Mode::Edit }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.and_then(|k| k.renderer).unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if model.mode == Mode::Edit && selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let mut all_entries = vec![];
                        let mut field_entries = k
                            .get_fields()
                            .iter()
                            .map(|(field_id, field)| Entry {
                                label: field.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), **field_id),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect();
                        // TODO: macros
                        let mut macro_entries = vec![
                            Entry {
                                label: "delete".to_string(),
                                description: "".to_string(),
                                action: Msg::DeleteItem,
                                valid_classes: vec![],
                            },
                            // TODO: should apply to literals too.
                            Entry {
                                label: "call".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_FUNCTION_CALL.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "array".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    path.clone(),
                                    Node {
                                        kind: RUST_ARRAY_TYPE.to_string(),
                                        value: "".to_string(),
                                        links: maplit::btreemap! {
                                            0 => vec![hash.clone()],
                                        },
                                    },
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                            Entry {
                                label: "move up".to_string(),
                                description: "".to_string(),
                                action: Msg::ReplaceNode(
                                    parent(&path).to_vec(),
                                    node.clone(),
                                    false,
                                ),
                                valid_classes: vec![],
                            },
                        ];
                        all_entries.append(&mut field_entries);
                        all_entries.append(&mut macro_entries);
                        all_entries
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let _onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {
                    <div class="pl-3 absolute bg-white">
                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ self.ondelete.clone() }
                            enabled=true
                        />
                    </div>
                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path;
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {
            <div
              class={ classes.join(" ") }
              tabindex="0"
              onclick={ onclick }
            >
              { inner }
            </div>
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = false;
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        if let Some(old_props) = &self.old_props {
            log::debug!("same path: {:?}", old_props.path == ctx.props().path);
            log::debug!("same model: {:?}", old_props.model == ctx.props().model);
            log::debug!("same hash: {:?}", old_props.hash == ctx.props().hash);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == ctx.props().updatemodel
            );
            return old_props.path != ctx.props().path
                || old_props.model != ctx.props().model
                || old_props.hash != ctx.props().hash;
        }
        self.old_props = Some(ctx.props().clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        if let Some(i) = input_node.cast::<HtmlInputElement>() {
            i.focus().unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use controller::Controller;
use cmd_class::basic::Basic;
use std::rc::Rc;
use std::cell::RefCell;

// The private representation of a node
struct _Node {
    id: u8,
    controller: Controller,
    class_basic: Option<Basic>
}

// reference type for the node
type NodeRef = Rc<RefCell<_Node>>;

// The public representation of a node, with some syntactic sugar.
#[derive(Clone)]
pub struct Node(NodeRef);

// The actual node implementation
impl Node {
    /// Creates a new node with no edges.
    pub fn new(contr: Controller, id: u8) -> Node {
        let node = Node(Rc::new(RefCell::new(_Node {
            id: id,
            controller: contr,
            class_basic: None
        })));

        node.discover_classes();

        node
    }

    /// Sets the available function classes for this node
    pub fn discover_classes(&self) {
        let mut this = &mut self.0.borrow_mut();

        // todo get the information from the device

        // basic is always available
        this.class_basic = Some(Basic::new(self.clone()));
    }

    /// returns the controller
    pub fn get_controller(&self) -> Controller {
        self.0.borrow().controller.clone()
    }

    /// returns the basic command class to interact with
    pub fn get_basic(&self) -> Option<Basic> {
        self.0.borrow_mut().class_basic.clone()
    }

    /// returns the id of the node in the zwave network
    pub fn get_id(&self) -> u8 {
        self.0.borrow().id.clone()
    }
}
<commit_msg>Added generic type identification closes #3<commit_after>use controller::Controller;
use cmd_class::basic::Basic;
use std::rc::Rc;
use std::cell::RefCell;
use num::FromPrimitive;
use msg::{Message, Type, Function};

// The private representation of a node
struct _Node {
    id: u8,
    controller: Controller,
    generic_type: GenericType,
    class_basic: Option<Basic>
}

// reference type for the node
type NodeRef = Rc<RefCell<_Node>>;

// The public representation of a node, with some syntactic sugar.
#[derive(Clone)]
pub struct Node(NodeRef);

// The actual node implementation
impl Node {
    /// Creates a new node with no edges.
    pub fn new(contr: Controller, id: u8) -> Node {
        let node = Node(Rc::new(RefCell::new(_Node {
            id: id,
            controller: contr,
            generic_type: GenericType::Unknown,
            class_basic: None
        })));

        node.discover_type();
        node.discover_classes();

        node
    }

    /// Sets the available type for this node
    pub fn discover_type(&self) {
        let mut this = &mut self.0.borrow_mut();

        // create a new message
        let msg = Message::new(Type::Request, Function::GetNodeProtocolInfo, vec!(this.id));

        // send the message to the ZWave driver
        let msg = unwrap_or_return!(this.controller.get_driver().write_and_read(msg).ok(), ());

        //check if the answer has the right length && is no error
        if msg.data.len() != 6 {
            this.generic_type = GenericType::Unknown;
            return;
        }

        // extract the delivered type
        this.generic_type = GenericType::from_u8(msg.data[4]).unwrap_or(GenericType::Unknown);
    }

    /// Sets the available function classes for this node
    pub fn discover_classes(&self) {
        let mut this = &mut self.0.borrow_mut();

        // todo get the information from the device

        // basic is always available
        this.class_basic = Some(Basic::new(self.clone()));
    }

    /// returns the controller
    pub fn get_controller(&self) -> Controller {
        self.0.borrow().controller.clone()
    }

    /// returns the basic command class to interact with
    pub fn get_basic(&self) -> Option<Basic> {
        self.0.borrow_mut().class_basic.clone()
    }

    /// returns the id of the node in the zwave network
    pub fn get_id(&self) -> u8 {
        self.0.borrow().id.clone()
    }

    // returns the generic type of the node
    pub fn get_generic_type(&self) -> GenericType {
        self.0.borrow().generic_type.clone()
    }
}

/// List of different Node types
enum_from_primitive! {
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum GenericType {
    Unknown = 0x00,
    BasicController = 0x01,
    StaticController = 0x02,
    BasicSlave = 0x03,
    RoutingSlave = 0x04,
    GenericThermostat = 0x08,
    BinarySwitch = 0x10,
    MultiLevelSwitch = 0x11,
    GenericSwitchRemote = 0x12,
    GenericSwitchToggle = 0x13,
    GenericSecurityPanel = 0x17,
    BinarySensor = 0x20,
    MultilevelSensor = 0x21,
    Meter = 0x31,
}
}
<|endoftext|>


<commit_before>use std::thread;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::ops::Deref;

// MIO
use mio::tcp::{listen, TcpListener, TcpStream};
use mio::util::Slab;
use mio::Socket;
use mio::buf::{ByteBuf, MutByteBuf, SliceBuf};
use mio::{Interest, PollOpt, NonBlock, Token, EventLoop, Handler, ReadHint};

use rand::{self, Rng};

// Data structures.
use store::Store;
use replica::Replica;
use state_machine::StateMachine;

// Cap'n Proto
use capnp::serialize_packed;
use capnp::{MessageBuilder, MessageReader, ReaderOptions, MallocMessageBuilder};
use messages_capnp::{
    rpc_request,
    rpc_response,
    client_request,
    client_response,
    request_vote_response,
    append_entries_response,
    append_entries_request,
};
use super::RaftError;

// MIO Tokens
const ELECTION_TIMEOUT: Token = Token(0);
const HEARTBEAT_TIMEOUT: Token = Token(1);
const LISTENER:  Token = Token(2);

const ELECTION_MIN: u64 = 150;
const ELECTION_MAX: u64 = 300;
const HEARTBEAT_DURATION: u64 = 50;

/// The Raft Distributed Consensus Algorithm requires two RPC calls to be available:
///
///   * `append_entries` which is used as both a heartbeat (with no payload) and the primary
///     interface for requests.
///   * `request_vote` which is used by candidates during campaigns to obtain a vote.
///
/// A `RaftNode` acts as a replicated state machine. The server's role in the cluster depends on it's
/// own status. It will maintain both volatile state (which can be safely lost) and persistent
/// state (which must be carefully stored and kept safe).
///
/// Currently, the `RaftNode` API is not well defined. **We are looking for feedback and suggestions.**
pub struct RaftNode<S, M> where S: Store, M: StateMachine {
    replica: Replica<S, M>,
    // Channels and Sockets
    listener: NonBlock<TcpListener>,
    connections: Slab<RaftConnection>,
}

/// The implementation of the RaftNode. In most use cases, creating a `RaftNode` should just be
/// done via `::new()`.
impl<S, M> RaftNode<S, M> where S: Store, M: StateMachine {

    /// Creates a new Raft node with the cluster members specified.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address of the new node.
    /// * `peers` - The address of all peers in the Raft cluster.
    /// * `store` - The persitent log store.
    /// * `state_machine` - The client state machine to which client commands will be applied.
    pub fn spawn(addr: SocketAddr,
                 peers: HashSet<SocketAddr>,
                 store: S,
                 state_machine: M) {
        // Create an event loop
        let mut event_loop = EventLoop::<RaftNode<S, M>>::new().unwrap();
        // Setup the socket, make it not block.
        let listener = listen(&addr).unwrap();
        listener.set_reuseaddr(true);
        event_loop.register(&listener, LISTENER).unwrap();
        let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
        event_loop.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
        event_loop.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION);
        let replica = Replica::new(addr, peers, store, state_machine);
        // Fire up the thread.
        thread::Builder::new().name(format!("RaftNode {}", addr)).spawn(move || {
            let mut raft_node = RaftNode {
                listener: listener,
                replica: replica,
                connections: Slab::new_starting_at(Token(2), 128),
            };
            event_loop.run(&mut raft_node).unwrap();
        }).unwrap();
    }
}

impl<S, M> Handler for RaftNode<S, M> where S: Store, M: StateMachine {

    type Message = ();
    type Timeout = Token;

    /// A registered IoHandle has available writing space.
    fn writable(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => unreachable!(),
            tok => {
                self.connections[tok].writable(reactor, &mut self.replica);
            }
        }
    }

    /// A registered IoHandle has available data to read
    fn readable(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token, hint: ReadHint) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => {
                let stream = self.listener.accept().unwrap().unwrap(); // Result<Option<_>,_>
                let conn = RaftConnection::new(stream);
                let tok = self.connections.insert(conn)
                    .ok().expect("Could not add connection to slab.");

                // Register the connection
                self.connections[tok].token = tok;
                reactor.register_opt(&self.connections[tok].stream, tok, Interest::readable(), PollOpt::edge() | PollOpt::oneshot())
                    .ok().expect("Could not register socket with event loop.");
            },
            tok => {
                self.connections[tok].readable(reactor, &mut self.replica);
            }
        }
    }

    /// A registered timer has expired
    fn timeout(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token) {
        let mut message = MallocMessageBuilder::new_default();
        let request = message.init_root::<rpc_request::Builder>();

        match token {
            ELECTION_TIMEOUT => {
                let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
                let _send_message = self.replica.election_timeout(request.init_request_vote());
                reactor.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
                // TODO: send messages if necessary
            },
            HEARTBEAT_TIMEOUT => {
                let _send_message = self.replica.heartbeat_timeout(request.init_append_entries());
                reactor.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
                // TODO: send messages if necessary
            },
            _ => unreachable!(),
        }
    }
}

struct RaftConnection {
    stream: NonBlock<TcpStream>,
    token: Token,
    interest: Interest,
    current_read: Option<MutByteBuf>,
    current_write: Option<ByteBuf>,
    next_write: Option<ByteBuf>,
}

impl RaftConnection {
    fn new(sock: NonBlock<TcpStream>) -> RaftConnection {
        RaftConnection {
            stream: sock,
            token: Token(-1),
            interest: Interest::hup(),
            current_read: None,
            current_write: None, // TODO: Size??
            next_write: None,
        }
    }

    fn writable<S, M>(&mut self, event_loop: &mut EventLoop<RaftNode<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<(), RaftError>
    where S: Store, M: StateMachine {
        // TODO
        Ok(())
    }

    fn readable<S, M>(&mut self, event_loop: &mut EventLoop<RaftNode<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<(), RaftError>
    where S: Store, M: StateMachine {
        let mut message = MallocMessageBuilder::new_default();
        let from = self.stream.peer_addr().unwrap();
        let message_reader = serialize_packed::new_reader_unbuffered(&mut self.stream.deref(), ReaderOptions::new())
            .unwrap();
        if let Ok(request) = message_reader.get_root::<rpc_request::Reader>() {
            // We will be responding.
            match request.which().unwrap() {
                // TODO: Move these into replica?
                rpc_request::Which::AppendEntries(Ok(call)) => {
                    let res = message.init_root::<append_entries_response::Builder>();
                    replica.append_entries_request(from, call, res);
                },
                rpc_request::Which::RequestVote(Ok(call)) => {
                    let res = message.init_root::<request_vote_response::Builder>();
                    replica.request_vote_request(from, call, res);
                },
                _ => unimplemented!(),
            }
            serialize_packed::write_packed_message_unbuffered(&mut self.stream.deref(), &mut message).unwrap();
            Ok(())
        } else if let Ok(response) = message_reader.get_root::<rpc_response::Reader>() {
            // We won't be responding. This is already a response.
            match response.which().unwrap() {
                rpc_response::Which::AppendEntries(Ok(call)) => {
                    let res = message.init_root::<append_entries_request::Builder>();
                    let send_message = replica.append_entries_response(from, call, res);
                    // TODO: send the AppendEntries requests if necessary
                    Ok(())
                },
                rpc_response::Which::RequestVote(Ok(call)) => {
                    let res = message.init_root::<append_entries_request::Builder>();
                    replica.request_vote_response(from, call, res);
                    // TODO: send the AppendEntries requests if necessary
                    // TODO: Can we just reset the timer?
                    unimplemented!();
                },
                _ => unimplemented!(),
            }
        } else if let Ok(client_req) = message_reader.get_root::<client_request::Reader>() {
            let mut should_die = false;
            // We will be responding.
            match client_req.which().unwrap() {
                client_request::Which::Append(Ok(call)) => {
                    let mut res = message.init_root::<client_response::Builder>();
                    replica.client_append(from, call, res)
                },
                client_request::Which::Die(Ok(call)) => {
                    should_die = true;
                    let mut res = message.init_root::<client_response::Builder>();
                    res.set_success(());
                    debug!("Got a Die request from Client({}). Reason: {}", from, call);
                },
                client_request::Which::LeaderRefresh(()) => {
                    let mut res = message.init_root::<client_response::Builder>();
                    replica.client_leader_refresh(from, res)
                },
                _ => unimplemented!(),
            }
            serialize_packed::write_packed_message_unbuffered(&mut self.stream.deref(), &mut message);
            // Do this here so that we can send the response.
            if should_die {
                panic!("Got a Die request.");
            }
            Ok(())
        } else {
            // It's something we don't understand.
            unimplemented!();
        }
    }
}
<commit_msg>CapnBuf<commit_after>use std::thread;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::ops::Deref;
use std::io;

// MIO
use mio::tcp::{listen, TcpListener, TcpStream};
use mio::util::Slab;
use mio::Socket;
use mio::buf::{ByteBuf, MutByteBuf, SliceBuf};
use mio::{Interest, PollOpt, NonBlock, Token, EventLoop, Handler, ReadHint};
use mio::buf::Buf;

use rand::{self, Rng};

// Data structures.
use store::Store;
use replica::Replica;
use state_machine::StateMachine;

// Cap'n Proto
use capnp::serialize_packed;
use capnp::{MessageBuilder, MessageReader, ReaderOptions, MallocMessageBuilder, Word};
use messages_capnp::{
    rpc_request,
    rpc_response,
    client_request,
    client_response,
    request_vote_response,
    append_entries_response,
    append_entries_request,
};
use super::RaftError;

// MIO Tokens
const ELECTION_TIMEOUT: Token = Token(0);
const HEARTBEAT_TIMEOUT: Token = Token(1);
const LISTENER:  Token = Token(2);

const ELECTION_MIN: u64 = 150;
const ELECTION_MAX: u64 = 300;
const HEARTBEAT_DURATION: u64 = 50;

/// The Raft Distributed Consensus Algorithm requires two RPC calls to be available:
///
///   * `append_entries` which is used as both a heartbeat (with no payload) and the primary
///     interface for requests.
///   * `request_vote` which is used by candidates during campaigns to obtain a vote.
///
/// A `RaftNode` acts as a replicated state machine. The server's role in the cluster depends on it's
/// own status. It will maintain both volatile state (which can be safely lost) and persistent
/// state (which must be carefully stored and kept safe).
///
/// Currently, the `RaftNode` API is not well defined. **We are looking for feedback and suggestions.**
pub struct RaftNode<S, M> where S: Store, M: StateMachine {
    replica: Replica<S, M>,
    // Channels and Sockets
    listener: NonBlock<TcpListener>,
    connections: Slab<RaftConnection>,
}

/// The implementation of the RaftNode. In most use cases, creating a `RaftNode` should just be
/// done via `::new()`.
impl<S, M> RaftNode<S, M> where S: Store, M: StateMachine {

    /// Creates a new Raft node with the cluster members specified.
    ///
    /// # Arguments
    ///
    /// * `addr` - The address of the new node.
    /// * `peers` - The address of all peers in the Raft cluster.
    /// * `store` - The persitent log store.
    /// * `state_machine` - The client state machine to which client commands will be applied.
    pub fn spawn(addr: SocketAddr,
                 peers: HashSet<SocketAddr>,
                 store: S,
                 state_machine: M) {
        // Create an event loop
        let mut event_loop = EventLoop::<RaftNode<S, M>>::new().unwrap();
        // Setup the socket, make it not block.
        let listener = listen(&addr).unwrap();
        listener.set_reuseaddr(true);
        event_loop.register(&listener, LISTENER).unwrap();
        let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
        event_loop.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
        event_loop.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION);
        let replica = Replica::new(addr, peers, store, state_machine);
        // Fire up the thread.
        thread::Builder::new().name(format!("RaftNode {}", addr)).spawn(move || {
            let mut raft_node = RaftNode {
                listener: listener,
                replica: replica,
                connections: Slab::new_starting_at(Token(2), 128),
            };
            event_loop.run(&mut raft_node).unwrap();
        }).unwrap();
    }
}

impl<S, M> Handler for RaftNode<S, M> where S: Store, M: StateMachine {

    type Message = ();
    type Timeout = Token;

    /// A registered IoHandle has available writing space.
    fn writable(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => unreachable!(),
            tok => {
                self.connections[tok].writable(reactor, &mut self.replica);
            }
        }
    }

    /// A registered IoHandle has available data to read
    fn readable(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token, hint: ReadHint) {
        match token {
            ELECTION_TIMEOUT => unreachable!(),
            HEARTBEAT_TIMEOUT => unreachable!(),
            LISTENER => {
                let stream = self.listener.accept().unwrap().unwrap(); // Result<Option<_>,_>
                let conn = RaftConnection::new(stream);
                let tok = self.connections.insert(conn)
                    .ok().expect("Could not add connection to slab.");

                // Register the connection
                self.connections[tok].token = tok;
                reactor.register_opt(&self.connections[tok].stream, tok, Interest::readable(), PollOpt::edge() | PollOpt::oneshot())
                    .ok().expect("Could not register socket with event loop.");
            },
            tok => {
                self.connections[tok].readable(reactor, &mut self.replica);
            }
        }
    }

    /// A registered timer has expired
    fn timeout(&mut self, reactor: &mut EventLoop<RaftNode<S, M>>, token: Token) {
        let mut message = MallocMessageBuilder::new_default();
        let request = message.init_root::<rpc_request::Builder>();

        match token {
            ELECTION_TIMEOUT => {
                let timeout = rand::thread_rng().gen_range::<u64>(ELECTION_MIN, ELECTION_MAX);
                let _send_message = self.replica.election_timeout(request.init_request_vote());
                reactor.timeout_ms(ELECTION_TIMEOUT, timeout).unwrap();
                // TODO: send messages if necessary
            },
            HEARTBEAT_TIMEOUT => {
                let _send_message = self.replica.heartbeat_timeout(request.init_append_entries());
                reactor.timeout_ms(HEARTBEAT_TIMEOUT, HEARTBEAT_DURATION).unwrap();
                // TODO: send messages if necessary
            },
            _ => unreachable!(),
        }
    }
}

struct RaftConnection {
    stream: NonBlock<TcpStream>,
    token: Token,
    interest: Interest,
    current_read: Option<CapnBuf>,
    current_write: Option<CapnBuf>,
    next_write: Option<CapnBuf>,
}

impl RaftConnection {
    fn new(sock: NonBlock<TcpStream>) -> RaftConnection {
        RaftConnection {
            stream: sock,
            token: Token(-1),
            interest: Interest::hup(),
            current_read: None,
            current_write: None, // TODO: Size??
            next_write: None,
        }
    }

    fn writable<S, M>(&mut self, event_loop: &mut EventLoop<RaftNode<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<(), RaftError>
    where S: Store, M: StateMachine {
        // TODO
        Ok(())
    }

    fn readable<S, M>(&mut self, event_loop: &mut EventLoop<RaftNode<S, M>>, replica: &mut Replica<S,M>)
                      -> Result<(), RaftError>
    where S: Store, M: StateMachine {
        let mut response_message = MallocMessageBuilder::new_default();
        let from = self.stream.peer_addr().unwrap();
        let message_reader = serialize_packed::new_reader_unbuffered(&mut self.stream.deref(), ReaderOptions::new())
            .unwrap();
        if let Ok(request) = message_reader.get_root::<rpc_request::Reader>() {
            // We will be responding.
            match request.which().unwrap() {
                // TODO: Move these into replica?
                rpc_request::Which::AppendEntries(Ok(call)) => {
                    let res = response_message.init_root::<append_entries_response::Builder>();
                    replica.append_entries_request(from, call, res);
                },
                rpc_request::Which::RequestVote(Ok(call)) => {
                    let res = response_message.init_root::<request_vote_response::Builder>();
                    replica.request_vote_request(from, call, res);
                },
                _ => unimplemented!(),
            }
            serialize_packed::write_packed_message_unbuffered(&mut self.stream.deref(), &mut response_message).unwrap();
            Ok(())
        } else if let Ok(response) = message_reader.get_root::<rpc_response::Reader>() {
            // We won't be responding. This is already a response.
            match response.which().unwrap() {
                rpc_response::Which::AppendEntries(Ok(call)) => {
                    let res = response_message.init_root::<append_entries_request::Builder>();
                    let send_message = replica.append_entries_response(from, call, res);
                    // TODO: send the AppendEntries requests if necessary
                    Ok(())
                },
                rpc_response::Which::RequestVote(Ok(call)) => {
                    let res = response_message.init_root::<append_entries_request::Builder>();
                    replica.request_vote_response(from, call, res);
                    // TODO: send the AppendEntries requests if necessary
                    // TODO: Can we just reset the timer?
                    unimplemented!();
                },
                _ => unimplemented!(),
            }
        } else if let Ok(client_req) = message_reader.get_root::<client_request::Reader>() {
            let mut should_die = false;
            // We will be responding.
            match client_req.which().unwrap() {
                client_request::Which::Append(Ok(call)) => {
                    let mut res = response_message.init_root::<client_response::Builder>();
                    replica.client_append(from, call, res)
                },
                client_request::Which::Die(Ok(call)) => {
                    should_die = true;
                    let mut res = response_message.init_root::<client_response::Builder>();
                    res.set_success(());
                    debug!("Got a Die request from Client({}). Reason: {}", from, call);
                },
                client_request::Which::LeaderRefresh(()) => {
                    let mut res = response_message.init_root::<client_response::Builder>();
                    replica.client_leader_refresh(from, res)
                },
                _ => unimplemented!(),
            }
            serialize_packed::write_packed_message_unbuffered(&mut self.stream.deref(), &mut message);
            // Do this here so that we can send the response.
            if should_die {
                panic!("Got a Die request.");
            }
            Ok(())
        } else {
            // It's something we don't understand.
            unimplemented!();
        }
    }
}

struct CapnBuf {
    // TODO: We almost certainly can just use segments!
    data: Vec<u8>,
    cursor: usize,
}

impl CapnBuf {
    fn new<T>(builder: &mut T) -> CapnBuf
    where T: MessageBuilder {
        let mut buf = CapnBuf {
            data: Vec::with_capacity(4096), // TODO: Make it smarter?
            cursor: 0,
        };
        let segments = builder.get_segments_for_output();
        for segment in segments {
            let bytes = Word::words_to_bytes(segment);
            for byte in bytes {
                buf.data.push(*byte)
            }
        }
        buf
    }
}

impl Buf for CapnBuf {
    fn remaining(&self) -> usize {
        self.data.len() - self.cursor
    }
    fn bytes(&self) -> &[u8] {
        &self.data[self.cursor..]
    }
    fn advance(&mut self, cnt: usize) {
        self.cursor += cnt;
    }
}

impl io::Read for CapnBuf {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if !self.has_remaining() {
            return Ok(0);
        }
        Ok(self.read_slice(buf))
    }
}
<|endoftext|>


<commit_before>use diesel::result::Error;
use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;

pub fn get_note(conn: &PgConnection, id: i32) -> Result<Note, Error> {
    use schema::notes;

    notes::table.find(id).first::<Note>(conn)
}

pub fn get_notes(conn: &PgConnection) -> Result<Vec<Note>, Error> {
    use schema::notes;

    notes::table.load::<Note>(conn)
}

pub fn create_note(conn: &PgConnection, note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::insert(&note).into(notes::table).get_result(conn)
}

pub fn delete_note(conn: &PgConnection, id: i32) -> Result<usize, Error> {
    use schema::notes;

    diesel::delete(notes::table.find(id))
        .execute(conn)
}

pub fn update_note(conn: &PgConnection, id: i32, updated_note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::update(notes::table.find(id))
        .set(&updated_note)
        .get_result::<Note>(conn)
}
<commit_msg>Moved up `use schema::notes;` to the top level<commit_after>use diesel::result::Error;
use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;
use schema::notes;

pub fn get_note(conn: &PgConnection, id: i32) -> Result<Note, Error> {
    notes::table
        .find(id)
        .first::<Note>(conn)
}

pub fn get_notes(conn: &PgConnection) -> Result<Vec<Note>, Error> {
    notes::table
        .load::<Note>(conn)
}

pub fn create_note(conn: &PgConnection, note: NoteData) -> Result<Note, Error> {
    diesel::insert(&note)
        .into(notes::table)
        .get_result(conn)
}

pub fn delete_note(conn: &PgConnection, id: i32) -> Result<usize, Error> {
    diesel::delete(notes::table.find(id))
        .execute(conn)
}

pub fn update_note(conn: &PgConnection, id: i32, updated_note: NoteData) -> Result<Note, Error> {
    diesel::update(notes::table
        .find(id))
        .set(&updated_note)
        .get_result::<Note>(conn)
}
<|endoftext|>


<commit_before>use hyper::Client;
use kuchiki::{Html, NodeRef, NodeDataRef, ElementData};
use string_cache::namespace::{Namespace, QualName};
use string_cache::atom::Atom;

#[derive(Debug)]
pub struct DictDef {
    pub word: String,
    pub def: String,
    pub example: Option<String>,
    pub upvote: i32,
    pub downvote: i32,
    pub contributor: String,
    pub date: String,
    pub id: String,
    pub sounds: Vec<String>
}

impl DictDef {
}

fn urban_dict_url (word: &str) -> String {
    let mut s = String::new();
    s.push_str("http://www.urbandictionary.com/define.php?term=");
    s.push_str(word);
    s
}

fn get_first_match(node: &NodeRef, selector: &str) -> Option<NodeDataRef<ElementData>> {
    if let Ok(mut eles) = node.select(selector) {
        eles.next()
    } else {
        None
    }
}

fn get_attribute_from_node(panel: &NodeRef, attr_name: &str) -> Option<String> {
    if let Some(panel_ele) = panel.as_element() {
        let attr_map = panel_ele.attributes.borrow();
        if let Some(attr_value) = attr_map.get(
            &QualName::new(Namespace(Atom::from_slice("")), Atom::from_slice(attr_name))) {
            return Some(attr_value.clone());
        }
    }
    None
}

fn get_node_text(node_data: &NodeDataRef<ElementData>) -> String {
    let node = node_data.as_node();
    node.text_contents()
}

fn json_list_to_strings(json: &str) -> Vec<String> {
    json[1..json.len()-1].split(", ").map(|s| s[1..s.len()-1].to_owned()).collect()
}

fn get_def_from_page_ele (panel: &NodeRef) -> Option<DictDef> {
    if let Some(word_ele) = get_first_match(panel, "a.word") {
        let word_text = get_node_text(&word_ele);

        let def_ele = get_first_match(panel, "div.meaning").unwrap();
        let def_text = get_node_text(&def_ele);

        let defid = get_attribute_from_node(panel, "data-defid").unwrap_or("".to_owned());

        let example = if let Some(example_ele) = get_first_match(panel, "div.example") {
            Some(get_node_text(&example_ele))
        } else {
            None
        };

        let author_ele = get_first_match(panel, "a.author").unwrap();
        let author = get_node_text(&author_ele);
        let date_node = author_ele.as_node().next_sibling().unwrap();
        let date_text = date_node.as_text().unwrap().borrow().trim().to_owned();

        let sounds_ele = get_first_match(panel, "a.play-sound").unwrap();
        let sounds_node = sounds_ele.as_node();
        let sounds_json_list = get_attribute_from_node(&sounds_node, "data-urls").unwrap_or("[]".to_owned());
        let sounds = json_list_to_strings(&sounds_json_list);

        Some(DictDef {
            word: word_text.trim().to_owned(),
            def: def_text.trim().to_owned(),
            example: example,
            upvote: 0,
            downvote: 0,
            contributor: author,
            date: date_text,
            id: defid,
            sounds: sounds
        })
    } else {
        None
    }
}

fn find_from_url(url: &str) -> Option<DictDef> {
    let client = Client::new();

    let mut response = client.get(url).send().unwrap();

    if let Ok(html) = Html::from_stream(&mut response) {
        let doc = html.parse();

        if let Some(panel_ele) = get_first_match(&doc, "div.def-panel") {
            let panel = panel_ele.as_node();
            return get_def_from_page_ele(panel);
        }
    }

    None
}

pub fn find_word_of_the_day() -> Option<DictDef> {
    find_from_url("http://www.urbandictionary.com/")
}

pub fn find_on_urban_dict (word: &str) -> Option<DictDef> {
    let url = urban_dict_url(word);
    find_from_url(&url)
}
<commit_msg>(fix) fixed panic when no sounds available<commit_after>use hyper::Client;
use kuchiki::{Html, NodeRef, NodeDataRef, ElementData};
use string_cache::namespace::{Namespace, QualName};
use string_cache::atom::Atom;

#[derive(Debug)]
pub struct DictDef {
    pub word: String,
    pub def: String,
    pub example: Option<String>,
    pub upvote: i32,
    pub downvote: i32,
    pub contributor: String,
    pub date: String,
    pub id: String,
    pub sounds: Vec<String>
}

impl DictDef {
}

fn urban_dict_url (word: &str) -> String {
    let mut s = String::new();
    s.push_str("http://www.urbandictionary.com/define.php?term=");
    s.push_str(word);
    s
}

fn get_first_match(node: &NodeRef, selector: &str) -> Option<NodeDataRef<ElementData>> {
    if let Ok(mut eles) = node.select(selector) {
        eles.next()
    } else {
        None
    }
}

fn get_attribute_from_node(panel: &NodeRef, attr_name: &str) -> Option<String> {
    if let Some(panel_ele) = panel.as_element() {
        let attr_map = panel_ele.attributes.borrow();
        if let Some(attr_value) = attr_map.get(
            &QualName::new(Namespace(Atom::from_slice("")), Atom::from_slice(attr_name))) {
            return Some(attr_value.clone());
        }
    }
    None
}

fn get_node_text(node_data: &NodeDataRef<ElementData>) -> String {
    let node = node_data.as_node();
    node.text_contents()
}

fn json_list_to_strings(json: &str) -> Vec<String> {
    json[1..json.len()-1].split(", ").map(|s| s[1..s.len()-1].to_owned()).collect()
}

fn get_def_from_page_ele (panel: &NodeRef) -> Option<DictDef> {
    if let Some(word_ele) = get_first_match(panel, "a.word") {
        let word_text = get_node_text(&word_ele);

        let def_ele = get_first_match(panel, "div.meaning").unwrap();
        let def_text = get_node_text(&def_ele);

        let defid = get_attribute_from_node(panel, "data-defid").unwrap_or("".to_owned());

        let example = if let Some(example_ele) = get_first_match(panel, "div.example") {
            Some(get_node_text(&example_ele))
        } else {
            None
        };

        let author_ele = get_first_match(panel, "a.author").unwrap();
        let author = get_node_text(&author_ele);
        let date_node = author_ele.as_node().next_sibling().unwrap();
        let date_text = date_node.as_text().unwrap().borrow().trim().to_owned();

        let sounds = if let Some(sounds_ele) = get_first_match(panel, "a.play-sound") {
            let sounds_node = sounds_ele.as_node();
            let sounds_json_list = get_attribute_from_node(&sounds_node, "data-urls").unwrap_or("[]".to_owned());
            json_list_to_strings(&sounds_json_list)
        } else {
            Vec::new()
        };

        Some(DictDef {
            word: word_text.trim().to_owned(),
            def: def_text.trim().to_owned(),
            example: example,
            upvote: 0,
            downvote: 0,
            contributor: author,
            date: date_text,
            id: defid,
            sounds: sounds
        })
    } else {
        None
    }
}

fn find_from_url(url: &str) -> Option<DictDef> {
    let client = Client::new();

    let mut response = client.get(url).send().unwrap();

    if let Ok(html) = Html::from_stream(&mut response) {
        let doc = html.parse();

        if let Some(panel_ele) = get_first_match(&doc, "div.def-panel") {
            let panel = panel_ele.as_node();
            return get_def_from_page_ele(panel);
        }
    }

    None
}

pub fn find_word_of_the_day() -> Option<DictDef> {
    find_from_url("http://www.urbandictionary.com/")
}

pub fn find_on_urban_dict (word: &str) -> Option<DictDef> {
    let url = urban_dict_url(word);
    find_from_url(&url)
}
<|endoftext|>


<commit_before>//! Defines types and traits related to transposition tables.

use std::cmp::min;
use moves::{Move, MoveDigest};
use value::*;
use depth::*;
use search_node::SearchNode;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// For the majority of chess positions our evaluations will be more
/// or less inaccurate, and there is nothing we can do about it. But
/// sometimes we know that a given evaluation is probably inaccurate,
/// and we know the sign of the error. `BoundType` defines the
/// direction of such **known inaccuracies**.
///
/// # Constants:
///
/// * `BOUND_EXACT` means that the evaluation is exact (as far as we know).
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation (as far as we know).
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation (as far as we know).
///
/// * `BOUND_NONE` means that the real value can be anything.
pub type BoundType = u8;

pub const BOUND_NONE: BoundType = 0;
pub const BOUND_LOWER: BoundType = 0b01;
pub const BOUND_UPPER: BoundType = 0b10;
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

/// A trait for transposition tables.
///
/// Chess programs, during their brute-force search, encounter the
/// same positions again and again, but from different sequences of
/// moves, which is called a "transposition". When the search
/// encounters a transposition, it is beneficial to "remember" what
/// was determined last time the position was examined, rather than
/// redoing the entire search again. For this reason, chess programs
/// have a transposition table, which is a large hash table storing
/// information about positions previously searched, how deeply they
/// were searched, and what we concluded about them. To implement your
/// own transposition table, you must define a type that implements
/// the `HashTable` trait.
pub trait HashTable: Sync + Send {
    type Entry: HashTableEntry;

    /// Creates a new transposition table.
    ///
    /// `size_mb` is the desired size in Mbytes.
    fn new(size_mb: Option<usize>) -> Self;

    /// Signals that a new search is about to begin.
    fn new_search(&self);

    /// Stores data by key.
    ///
    /// After being stored, the data can be retrieved by `probe`. This
    /// is not guaranteed though, because the entry might have been
    /// overwritten in the meantime.
    fn store(&self, key: u64, data: Self::Entry);

    /// Probes for data by key.
    fn probe(&self, key: u64) -> Option<Self::Entry>;

    /// Removes all entries in the table.
    fn clear(&self);

    /// Extracts the principal variation for a given position.
    ///
    /// The principal variation (PV) is the sequence of moves that the
    /// engine considers best and therefore expects to be played.
    fn extract_pv<T: SearchNode>(&self, position: &T) -> Variation {
        let mut p = position.clone();
        let mut our_turn = true;
        let mut moves = Vec::with_capacity(32);
        let mut root_value = VALUE_UNKNOWN;
        let mut value = VALUE_MAX;
        let mut bound = BOUND_UPPER;
        let mut depth = DEPTH_MAX + 1;

        'move_extraction: while let Some(e) = self.probe(p.hash()) {
            depth = min(depth - 1, e.depth());

            if e.bound() == BOUND_EXACT || root_value == VALUE_UNKNOWN && e.bound() != BOUND_NONE {
                // In half of the cases the value is from other side's perspective.
                if our_turn {
                    value = e.value();
                    bound = e.bound();
                } else {
                    value = -e.value();
                    bound = match e.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        b => b,
                    };
                }
                assert!(value != VALUE_UNKNOWN);

                // Set the root value on the first iteration.
                if root_value == VALUE_UNKNOWN {
                    root_value = value;
                }

                // Consider adding current entry's hash move to the
                // PV. There are 2 conditions for this:
                //
                // 1) The depth limit has not been reached yet.
                // 2) The value has not diverged from the root value.
                if depth > 0 &&
                   match root_value {
                    v if v < VALUE_EVAL_MIN => v as isize == value as isize + moves.len() as isize,
                    v if v > VALUE_EVAL_MAX => v as isize == value as isize - moves.len() as isize,
                    v => v == value,
                } {
                    // Verify that the hash move is legal.
                    if let Some(m) = p.try_move_digest(e.move_digest()) {
                        if p.do_move(m) {
                            moves.push(m);

                            // Note: we continue expanding the PV only on best moves.
                            if e.bound() == BOUND_EXACT {
                                our_turn = !our_turn;
                                continue 'move_extraction;
                            }
                        }
                    }
                }
            }
            break 'move_extraction;
        }

        Variation {
            value: if root_value != VALUE_UNKNOWN {
                root_value
            } else {
                value
            },
            bound: bound,
            moves: moves,
        }
    }
}

/// A trait for transposition table entries.
pub trait HashTableEntry: Copy + Send {
    /// Creates a new instance.
    ///
    /// * `value` -- The value assigned to the position. Must not be
    ///   `VALUE_UNKNOWN`.
    ///
    /// * `bound` -- The accuracy of the assigned value.
    ///
    /// * `depth` -- The depth of search. Must be between `DEPTH_MIN`
    ///   and `DEPTH_MAX`.
    ///
    /// * `move_digest` -- Best or refutation move digest, or
    ///   `MoveDigest::invalid()` if no move is available.
    fn new(value: Value, bound: BoundType, depth: Depth, move_digest: MoveDigest) -> Self;

    /// Creates a new instance.
    ///
    /// The only difference between this function and `new` is that
    /// this function requires one additional parameter:
    ///
    /// * `static_eval` -- Position's static evaluation, or
    ///   `VALUE_UNKNOWN`.
    ///
    /// **Important note:** `static_eval` will be ignored if there is
    /// no field allotted for it in the underlying memory structure.
    fn with_static_eval(value: Value,
                        bound: BoundType,
                        depth: Depth,
                        move_digest: MoveDigest,
                        static_eval: Value)
                        -> Self;

    /// Returns the value assigned to the position.
    fn value(&self) -> Value;

    /// Returns the accuracy of the assigned value.
    fn bound(&self) -> BoundType;

    /// Returns the search depth for the assigned value.
    fn depth(&self) -> Depth;

    /// Returns best or refutation move digest, or
    /// `MoveDigest::invalid()` if no move is available.
    fn move_digest(&self) -> MoveDigest;

    /// Returns position's static evaluation, or `VALUE_UNKNOWN`.
    fn static_eval(&self) -> Value;

    /// Sets a new best or refutation move digest.
    ///
    /// Transposition tables may use this method when they overwrite
    /// an old record for the same position, and want to keep the move
    /// from the old record.
    fn set_move_digest(&mut self, move_digest: MoveDigest);

    /// Returns the relative importance of the entry.
    ///
    /// Transposition tables may use this method to improve their
    /// record replacement strategy. Normally, when possible, entries
    /// with lower `importance` will be replaced before entries with
    /// higher `importance`. Therefore this method should try to
    /// return higher values for entries that are move likely to save
    /// CPU work in the future. For example, positions analyzed to a
    /// higher depth are probably more "important" than those analyzed
    /// to a lower depth.
    #[inline]
    fn importance(&self) -> i16 {
        match self.bound() {
            BOUND_EXACT => self.depth() as i16,
            BOUND_NONE => 0,
            _ => self.depth() as i16 - 1,
        }
    }
}

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    ///
    /// The value is from the point of view of player that has the
    /// move in the starting position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}
<commit_msg>minor correction in importance()<commit_after>//! Defines types and traits related to transposition tables.

use std::cmp::min;
use moves::{Move, MoveDigest};
use value::*;
use depth::*;
use search_node::SearchNode;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// For the majority of chess positions our evaluations will be more
/// or less inaccurate, and there is nothing we can do about it. But
/// sometimes we know that a given evaluation is probably inaccurate,
/// and we know the sign of the error. `BoundType` defines the
/// direction of such **known inaccuracies**.
///
/// # Constants:
///
/// * `BOUND_EXACT` means that the evaluation is exact (as far as we know).
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation (as far as we know).
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation (as far as we know).
///
/// * `BOUND_NONE` means that the real value can be anything.
pub type BoundType = u8;

pub const BOUND_NONE: BoundType = 0;
pub const BOUND_LOWER: BoundType = 0b01;
pub const BOUND_UPPER: BoundType = 0b10;
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

/// A trait for transposition tables.
///
/// Chess programs, during their brute-force search, encounter the
/// same positions again and again, but from different sequences of
/// moves, which is called a "transposition". When the search
/// encounters a transposition, it is beneficial to "remember" what
/// was determined last time the position was examined, rather than
/// redoing the entire search again. For this reason, chess programs
/// have a transposition table, which is a large hash table storing
/// information about positions previously searched, how deeply they
/// were searched, and what we concluded about them. To implement your
/// own transposition table, you must define a type that implements
/// the `HashTable` trait.
pub trait HashTable: Sync + Send {
    type Entry: HashTableEntry;

    /// Creates a new transposition table.
    ///
    /// `size_mb` is the desired size in Mbytes.
    fn new(size_mb: Option<usize>) -> Self;

    /// Signals that a new search is about to begin.
    fn new_search(&self);

    /// Stores data by key.
    ///
    /// After being stored, the data can be retrieved by `probe`. This
    /// is not guaranteed though, because the entry might have been
    /// overwritten in the meantime.
    fn store(&self, key: u64, data: Self::Entry);

    /// Probes for data by key.
    fn probe(&self, key: u64) -> Option<Self::Entry>;

    /// Removes all entries in the table.
    fn clear(&self);

    /// Extracts the principal variation for a given position.
    ///
    /// The principal variation (PV) is the sequence of moves that the
    /// engine considers best and therefore expects to be played.
    fn extract_pv<T: SearchNode>(&self, position: &T) -> Variation {
        let mut p = position.clone();
        let mut our_turn = true;
        let mut moves = Vec::with_capacity(32);
        let mut root_value = VALUE_UNKNOWN;
        let mut value = VALUE_MAX;
        let mut bound = BOUND_UPPER;
        let mut depth = DEPTH_MAX + 1;

        'move_extraction: while let Some(e) = self.probe(p.hash()) {
            depth = min(depth - 1, e.depth());

            if e.bound() == BOUND_EXACT || root_value == VALUE_UNKNOWN && e.bound() != BOUND_NONE {
                // In half of the cases the value is from other side's perspective.
                if our_turn {
                    value = e.value();
                    bound = e.bound();
                } else {
                    value = -e.value();
                    bound = match e.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        b => b,
                    };
                }
                assert!(value != VALUE_UNKNOWN);

                // Set the root value on the first iteration.
                if root_value == VALUE_UNKNOWN {
                    root_value = value;
                }

                // Consider adding current entry's hash move to the
                // PV. There are 2 conditions for this:
                //
                // 1) The depth limit has not been reached yet.
                // 2) The value has not diverged from the root value.
                if depth > 0 &&
                   match root_value {
                    v if v < VALUE_EVAL_MIN => v as isize == value as isize + moves.len() as isize,
                    v if v > VALUE_EVAL_MAX => v as isize == value as isize - moves.len() as isize,
                    v => v == value,
                } {
                    // Verify that the hash move is legal.
                    if let Some(m) = p.try_move_digest(e.move_digest()) {
                        if p.do_move(m) {
                            moves.push(m);

                            // Note: we continue expanding the PV only on best moves.
                            if e.bound() == BOUND_EXACT {
                                our_turn = !our_turn;
                                continue 'move_extraction;
                            }
                        }
                    }
                }
            }
            break 'move_extraction;
        }

        Variation {
            value: if root_value != VALUE_UNKNOWN {
                root_value
            } else {
                value
            },
            bound: bound,
            moves: moves,
        }
    }
}

/// A trait for transposition table entries.
pub trait HashTableEntry: Copy + Send {
    /// Creates a new instance.
    ///
    /// * `value` -- The value assigned to the position. Must not be
    ///   `VALUE_UNKNOWN`.
    ///
    /// * `bound` -- The accuracy of the assigned value.
    ///
    /// * `depth` -- The depth of search. Must be between `DEPTH_MIN`
    ///   and `DEPTH_MAX`.
    ///
    /// * `move_digest` -- Best or refutation move digest, or
    ///   `MoveDigest::invalid()` if no move is available.
    fn new(value: Value, bound: BoundType, depth: Depth, move_digest: MoveDigest) -> Self;

    /// Creates a new instance.
    ///
    /// The only difference between this function and `new` is that
    /// this function requires one additional parameter:
    ///
    /// * `static_eval` -- Position's static evaluation, or
    ///   `VALUE_UNKNOWN`.
    ///
    /// **Important note:** `static_eval` will be ignored if there is
    /// no field allotted for it in the underlying memory structure.
    fn with_static_eval(value: Value,
                        bound: BoundType,
                        depth: Depth,
                        move_digest: MoveDigest,
                        static_eval: Value)
                        -> Self;

    /// Returns the value assigned to the position.
    fn value(&self) -> Value;

    /// Returns the accuracy of the assigned value.
    fn bound(&self) -> BoundType;

    /// Returns the search depth for the assigned value.
    fn depth(&self) -> Depth;

    /// Returns best or refutation move digest, or
    /// `MoveDigest::invalid()` if no move is available.
    fn move_digest(&self) -> MoveDigest;

    /// Returns position's static evaluation, or `VALUE_UNKNOWN`.
    fn static_eval(&self) -> Value;

    /// Sets a new best or refutation move digest.
    ///
    /// Transposition tables may use this method when they overwrite
    /// an old record for the same position, and want to keep the move
    /// from the old record.
    fn set_move_digest(&mut self, move_digest: MoveDigest);

    /// Returns the relative importance of the entry.
    ///
    /// Transposition tables may use this method to improve their
    /// record replacement strategy. Normally, when possible, entries
    /// with lower `importance` will be replaced before entries with
    /// higher `importance`. Therefore this method should try to
    /// return higher values for entries that are move likely to save
    /// CPU work in the future. For example, positions analyzed to a
    /// higher depth are probably more "important" than those analyzed
    /// to a lower depth.
    #[inline]
    fn importance(&self) -> i16 {
        match self.bound() {
            BOUND_EXACT => self.depth() as i16 + 1,
            BOUND_NONE => -1,
            _ => self.depth() as i16,
        }
    }
}

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    ///
    /// The value is from the point of view of player that has the
    /// move in the starting position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}
<|endoftext|>


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

#![allow(dead_code, unused_imports)]

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

use std::path::Path;
use std::process::Command;

const GRPC_VERSION: &'static str = "1.2.5";
const ZLIB_VERSION: &'static str = "1.2.8";
const BORINGSSL_GIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";

#[cfg(not(feature = "static-link"))]
fn build_or_link_grpc(cc: &mut gcc::Config) {
    if let Ok(lib) = pkg_config::Config::new()
        .atleast_version(GRPC_VERSION)
        .statik(true)
        .probe("grpc_unsecure") {
        for inc_path in lib.include_paths {
            cc.include(inc_path);
        }
    } else {
        panic!("can't find dynamic grpc library");
    }
}

#[cfg(feature = "static-link")]
fn build_or_link_grpc(cc: &mut gcc::Config) {
    fn wget(url: &str, out: &str) -> Result<(), String> {
        Command::new("wget")
            .args(&["-q", "-c", "-O", out, url])
            .status()
            .map_err(|err| format!("wget execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("wget exit with {}", status))
                }
            })
    }

    fn tar_xf(file: &str) -> Result<(), String> {
        Command::new("tar")
            .args(&["zxf", file])
            .status()
            .map_err(|err| format!("tar execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("tar exit with {}", status))
                }
            })
    }

    if !Path::new(&format!("grpc-{}", GRPC_VERSION)).exists() {
        wget(&format!("https://github.com/grpc/grpc/archive/v{}.tar.gz",
                      GRPC_VERSION),
             "grpc.tar.gz")
            .and_then(|_| tar_xf("grpc.tar.gz"))
            .unwrap();
    }

    if !Path::new(&format!("zlib-{}", ZLIB_VERSION)).exists() {
        wget(&format!("https://github.com/madler/zlib/archive/v{}.tar.gz",
                      ZLIB_VERSION),
             "zlib.tar.gz")
            .and_then(|_| tar_xf("zlib.tar.gz"))
            .unwrap();
    }

    if !Path::new(&format!("boringssl-{}", BORINGSSL_GIT_HASH)).exists() {
        wget(&format!("https://github.com/google/boringssl/archive/{}.tar.gz",
                      BORINGSSL_GIT_HASH),
             "boringssl.tar.gz")
            .and_then(|_| tar_xf("boringssl.tar.gz"))
            .unwrap();
    }

    let dst = cmake::Config::new(format!("zlib-{}", ZLIB_VERSION)).build();

    println!("cargo:rustc-link-search=native={}/lib", dst.display());

    let dst = cmake::Config::new(format!("grpc-{}", GRPC_VERSION))
        .define("ZLIB_ROOT_DIR", format!("../zlib-{}", ZLIB_VERSION)) // relative to grpc dir
        .define("BORINGSSL_ROOT_DIR", format!("../boringssl-{}", BORINGSSL_GIT_HASH))
        .build_target("grpc_unsecure")
        .build();

    cc.include(format!("grpc-{}/include", GRPC_VERSION));

    println!("cargo:rustc-link-search=native={}/build", dst.display());
    println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
             dst.display());

    println!("cargo:rustc-link-lib=static=z");
    println!("cargo:rustc-link-lib=static=gpr");
    println!("cargo:rustc-link-lib=static=grpc_unsecure");
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let mut cc = gcc::Config::new();

    build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<commit_msg>*: address commit<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;
extern crate pkg_config;

const GRPC_VERSION: &'static str = "1.2.5";

#[cfg(not(feature = "static-link"))]
mod imp {
    use super::GRPC_VERSION;

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {
        if let Ok(lib) = ::pkg_config::Config::new()
            .atleast_version(GRPC_VERSION)
            .statik(true)
            .probe("grpc_unsecure") {
            for inc_path in lib.include_paths {
                cc.include(inc_path);
            }
        } else {
            panic!("can't find dynamic grpc library");
        }
    }
}

#[cfg(feature = "static-link")]
mod imp {
    use std::path::Path;
    use std::process::Command;

    use super::GRPC_VERSION;

    const ZLIB_VERSION: &'static str = "1.2.8";
    const BORINGSSL_GIT_HASH: &'static str = "78684e5b222645828ca302e56b40b9daff2b2d27";

    fn wget(url: &str, out: &str) -> Result<(), String> {
        Command::new("wget")
            .args(&["-q", "-c", "-O", out, url])
            .status()
            .map_err(|err| format!("wget execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("wget exit with {}", status))
                }
            })
    }

    fn tar_xf(file: &str) -> Result<(), String> {
        Command::new("tar")
            .args(&["zxf", file])
            .status()
            .map_err(|err| format!("tar execute failed: {}", err))
            .and_then(|status| {
                if status.success() {
                    Ok(())
                } else {
                    Err(format!("tar exit with {}", status))
                }
            })
    }

    pub fn build_or_link_grpc(cc: &mut ::gcc::Config) {

        if !Path::new(&format!("grpc-{}", GRPC_VERSION)).exists() {
            wget(&format!("https://github.com/grpc/grpc/archive/v{}.tar.gz",
                          GRPC_VERSION),
                 "grpc.tar.gz")
                .and_then(|_| tar_xf("grpc.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("zlib-{}", ZLIB_VERSION)).exists() {
            wget(&format!("https://github.com/madler/zlib/archive/v{}.tar.gz",
                          ZLIB_VERSION),
                 "zlib.tar.gz")
                .and_then(|_| tar_xf("zlib.tar.gz"))
                .unwrap();
        }

        if !Path::new(&format!("boringssl-{}", BORINGSSL_GIT_HASH)).exists() {
            wget(&format!("https://github.com/google/boringssl/archive/{}.tar.gz",
                          BORINGSSL_GIT_HASH),
                 "boringssl.tar.gz")
                .and_then(|_| tar_xf("boringssl.tar.gz"))
                .unwrap();
        }

        let dst = ::cmake::Config::new(format!("zlib-{}", ZLIB_VERSION)).build();

        println!("cargo:rustc-link-search=native={}/lib", dst.display());

        let dst = ::cmake::Config::new(format!("grpc-{}", GRPC_VERSION))
        .define("ZLIB_ROOT_DIR", format!("../zlib-{}", ZLIB_VERSION)) // relative to grpc dir
        .define("BORINGSSL_ROOT_DIR", format!("../boringssl-{}", BORINGSSL_GIT_HASH))
        .build_target("grpc_unsecure")
        .build();

        cc.include(format!("grpc-{}/include", GRPC_VERSION));

        println!("cargo:rustc-link-search=native={}/build", dst.display());
        println!("cargo:rustc-link-search=native={}/build/third_party/zlib",
                 dst.display());

        println!("cargo:rustc-link-lib=static=z");
        println!("cargo:rustc-link-lib=static=gpr");
        println!("cargo:rustc-link-lib=static=grpc_unsecure");
    }
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let mut cc = ::gcc::Config::new();

    imp::build_or_link_grpc(&mut cc);

    cc.file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<|endoftext|>