<commit_before>#[macro_use]
extern crate criterion;

use criterion::Criterion;
use libxml::parser::Parser;
use libxml::readonly::RoNode;
use libxml::tree::NodeType;
use rayon::prelude::*;

// -- workhorse functions
// not *quite* classic depth-first search, since we keep all children at the current level in memory,
// but certainly DFS-order for traversal

fn dfs_single(node: RoNode) -> i32 {
  1 + node
    .get_child_nodes()
    .into_iter()
    .map(dfs_single)
    .sum::<i32>()
}

fn dfs_parallel(node: RoNode) -> i32 {
  1 + node
    .get_child_nodes()
    .into_par_iter()
    .map(dfs_parallel)
    .sum::<i32>()
}

fn dfs_single_work2(node: RoNode) -> (i32, usize) {
  if node.get_type() == Some(NodeType::TextNode) {
    (1, node.get_content().len())
  } else {
    node
      .get_child_nodes()
      .into_iter()
      .map(dfs_single_work2)
      .fold((1, 0), |acc, x| (acc.0 + x.0, acc.1 + x.1))
  }
}

fn dfs_parallel_work2(node: RoNode) -> (i32, usize) {
  if node.get_type() == Some(NodeType::TextNode) {
    (1, node.get_content().len())
  } else {
    let dfs_work = node
      .get_child_nodes()
      .into_par_iter()
      .map(dfs_parallel_work2)
      .reduce(|| (0, 0), |acc, x| (acc.0 + x.0, acc.1 + x.1));
    (dfs_work.0 + 1, dfs_work.1)
  }
}

// --- bencher functions

fn bench_single_thread(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("single thread DFS count", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_single(root), 4_690_647)
    })
  });
}

fn bench_single_thread_work2(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("single thread DFS count+length", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_single_work2(root), (4_690_647, 81_286_567))
    })
  });
}

fn bench_multi_thread(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("multi thread DFS count", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_parallel(root), 4_690_647);
    })
  });
}

fn bench_multi_thread_work2(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("multi thread DFS count+length", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_parallel_work2(root), (4_690_647, 81_286_567))
    })
  });
}
criterion_group!(
  name = benches;
  config = Criterion::default().sample_size(10);
  targets = bench_single_thread, bench_single_thread_work2, bench_multi_thread, bench_multi_thread_work2
);

criterion_main!(benches);
<commit_msg>bench against the Node implementation in single-thread mode<commit_after>#[macro_use]
extern crate criterion;

use criterion::Criterion;
use libxml::parser::Parser;
use libxml::readonly::RoNode;
use libxml::tree::{Node, NodeType};
use rayon::prelude::*;

// -- workhorse functions
// not *quite* classic depth-first search, since we keep all children at the current level in memory,
// but certainly DFS-order for traversal

fn dfs_single_classic(node: Node) -> i32 {
  1 + node
    .get_child_nodes()
    .into_iter()
    .map(dfs_single_classic)
    .sum::<i32>()
}

fn dfs_single(node: RoNode) -> i32 {
  1 + node
    .get_child_nodes()
    .into_iter()
    .map(dfs_single)
    .sum::<i32>()
}

fn dfs_parallel(node: RoNode) -> i32 {
  1 + node
    .get_child_nodes()
    .into_par_iter()
    .map(dfs_parallel)
    .sum::<i32>()
}

fn dfs_single_classic_work2(node: Node) -> (i32, usize) {
  if node.get_type() == Some(NodeType::TextNode) {
    (1, node.get_content().len())
  } else {
    node
      .get_child_nodes()
      .into_iter()
      .map(dfs_single_classic_work2)
      .fold((1, 0), |acc, x| (acc.0 + x.0, acc.1 + x.1))
  }
}

fn dfs_single_work2(node: RoNode) -> (i32, usize) {
  if node.get_type() == Some(NodeType::TextNode) {
    (1, node.get_content().len())
  } else {
    node
      .get_child_nodes()
      .into_iter()
      .map(dfs_single_work2)
      .fold((1, 0), |acc, x| (acc.0 + x.0, acc.1 + x.1))
  }
}

fn dfs_parallel_work2(node: RoNode) -> (i32, usize) {
  if node.get_type() == Some(NodeType::TextNode) {
    (1, node.get_content().len())
  } else {
    let dfs_work = node
      .get_child_nodes()
      .into_par_iter()
      .map(dfs_parallel_work2)
      .reduce(|| (0, 0), |acc, x| (acc.0 + x.0, acc.1 + x.1));
    (dfs_work.0 + 1, dfs_work.1)
  }
}

// --- bencher functions
// to get big.xml download, unpack and rename:
// http://www.ins.cwi.nl/projects/xmark/Assets/standard.gz
// or use your own XML sample
fn bench_single_thread_classic(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("single thread DFS count", move |b| {
    b.iter(|| {
      let root = doc.get_root_element().unwrap();
      assert_eq!(dfs_single_classic(root), 4_690_647)
    })
  });
}

fn bench_single_thread_classic_work2(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("single thread DFS count+length", move |b| {
    b.iter(|| {
      let root = doc.get_root_element().unwrap();
      assert_eq!(dfs_single_classic_work2(root), (4_690_647, 81_286_567))
    })
  });
}

fn bench_single_thread(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("read-only single thread DFS count", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_single(root), 4_690_647)
    })
  });
}

fn bench_single_thread_work2(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("read-only single thread DFS count+length", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_single_work2(root), (4_690_647, 81_286_567))
    })
  });
}

fn bench_multi_thread(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("read-only multi thread DFS count", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_parallel(root), 4_690_647);
    })
  });
}

fn bench_multi_thread_work2(c: &mut Criterion) {
  let parser = Parser::default();
  let doc = parser.parse_file("benches/big.xml").unwrap();
  c.bench_function("read-only multi thread DFS count+length", move |b| {
    b.iter(|| {
      let root = doc.get_root_readonly().unwrap();
      assert_eq!(dfs_parallel_work2(root), (4_690_647, 81_286_567))
    })
  });
}
criterion_group!(
  name = benches;
  config = Criterion::default().sample_size(10);
  targets = bench_single_thread_classic,  bench_single_thread_classic_work2, bench_single_thread, bench_single_thread_work2, bench_multi_thread, bench_multi_thread_work2
);

criterion_main!(benches);
<|endoftext|>


<commit_before>//! Provides drilldown related procedures.
//! This module will be used with `SelectCommand` type by `+` operation
//! to concatinate `select` and `drilldown` queries.
pub mod drilldown;
pub mod labeled_drilldown;
pub mod drilldownable;
pub mod labeled_drilldownable;
pub mod labeled_drilldown_sequencable;
pub mod drilldown_builder;
pub mod labeled_drilldown_builder;
pub mod labeled_drilldown_sequence_builder;
pub mod fragmentable;
pub mod pseudo_table;
pub mod staged_columns;
pub mod staged_columns_builder;
pub mod staged_columnable;
<commit_msg>selectable: Update rustdoc<commit_after>//! Provides drilldown related procedures.
//! This module will be used with `SelectCommand` type by `+` operation
//! to concatinate `select` and `drilldown` queries.
//!
//! Also this module support `pseudo_table` and `staged_columns` manupilation.
pub mod drilldown;
pub mod labeled_drilldown;
pub mod drilldownable;
pub mod labeled_drilldownable;
pub mod labeled_drilldown_sequencable;
pub mod drilldown_builder;
pub mod labeled_drilldown_builder;
pub mod labeled_drilldown_sequence_builder;
/// `fragmentable` module provides making query fragment operation.
/// It is useful to make composable query elements.
/// It makes `select` query to be composable against drilldown related query and `staged_columns` query.
pub mod fragmentable;
pub mod pseudo_table;
pub mod staged_columns;
pub mod staged_columns_builder;
pub mod staged_columnable;
<|endoftext|>


<commit_before>// Copyright (c) 2014 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate pnet;

/// A simple echo server for packets using a test protocol

use std::iter::repeat;

use pnet::old_packet::{MutablePacket, Packet};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::old_packet::udp::{MutableUdpHeader, UdpPacket};
use pnet::transport::{transport_channel, udp_header_iter};
use pnet::transport::TransportProtocol::{Ipv4};
use pnet::transport::TransportChannelType::{Layer4};

fn main() {
    let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Test1));

    // Create a new transport channel, dealing with layer 4 packets on a test protocol
    // It has a receive buffer of 4096 bytes.
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel: {}", e)
    };

    // We treat received packets as if they were UDP packets
    let mut iter = udp_header_iter(&mut rx);
    loop {
        match iter.next() {
            Ok((packet, addr)) => {
                // Allocate enough space for a new packet
                let mut vec: Vec<u8> = repeat(0u8).take(packet.packet().len()).collect();
                let mut new_packet = MutableUdpHeader::new(vec.as_mut_slice());

                // Create a clone of the original packet
                new_packet.clone_from(packet);

                // Switch the source and destination ports
                new_packet.set_source(packet.get_destination());
                new_packet.set_destination(packet.get_source());

                // Send the packet
                match tx.send_to(new_packet, addr) {
                    Ok(n) => assert_eq!(n, packet.packet().len()),
                    Err(e) => panic!("failed to send packet: {}", e)
                }
            },
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}

<commit_msg>Use replacement for deprecated `Vec#as_mut_slice`.<commit_after>// Copyright (c) 2014 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate pnet;

/// A simple echo server for packets using a test protocol

use std::iter::repeat;

use pnet::old_packet::{MutablePacket, Packet};
use pnet::old_packet::ip::IpNextHeaderProtocols;
use pnet::old_packet::udp::{MutableUdpHeader, UdpPacket};
use pnet::transport::{transport_channel, udp_header_iter};
use pnet::transport::TransportProtocol::{Ipv4};
use pnet::transport::TransportChannelType::{Layer4};

fn main() {
    let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Test1));

    // Create a new transport channel, dealing with layer 4 packets on a test protocol
    // It has a receive buffer of 4096 bytes.
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel: {}", e)
    };

    // We treat received packets as if they were UDP packets
    let mut iter = udp_header_iter(&mut rx);
    loop {
        match iter.next() {
            Ok((packet, addr)) => {
                // Allocate enough space for a new packet
                let mut vec: Vec<u8> = repeat(0u8).take(packet.packet().len()).collect();
                let mut new_packet = MutableUdpHeader::new(&mut vec[..]);

                // Create a clone of the original packet
                new_packet.clone_from(packet);

                // Switch the source and destination ports
                new_packet.set_source(packet.get_destination());
                new_packet.set_destination(packet.get_source());

                // Send the packet
                match tx.send_to(new_packet, addr) {
                    Ok(n) => assert_eq!(n, packet.packet().len()),
                    Err(e) => panic!("failed to send packet: {}", e)
                }
            },
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}

<|endoftext|>


<commit_before>#![warn(unused_variables)]
extern crate rust_base58;
extern crate serde_json;
extern crate zmq;

use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::thread;

use commands::{Command, CommandExecutor};
use commands::agent::AgentCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use utils::json::{JsonDecodable, JsonEncodable};
use utils::sequence::SequenceUtils;

struct RemoteAgent {
    socket: zmq::Socket,
    addr: String,
    public_key: Vec<u8>,
    secret_key: Vec<u8>,
    server_key: Vec<u8>,
    conn_handle: i32,
}

struct AgentWorker {
    cmd_socket: zmq::Socket,
    agent_connections: Vec<RemoteAgent>,
}

struct Agent {
    cmd_socket: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

impl Drop for Agent {
    fn drop(&mut self) {
        trace!("agent drop >>");
        self.cmd_socket.send_str(AgentWorkerCommand::Exit.to_json().unwrap().as_str(), zmq::DONTWAIT).unwrap(); //TODO
        self.worker.take().unwrap().join().unwrap();
        trace!("agent drop <<");
    }
}

pub struct AgentService {
    agent: RefCell<Option<Agent>>,
}

impl Agent {
    pub fn new() -> Agent {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("agent", true).unwrap();
        let mut worker = AgentWorker {
            cmd_socket: recv_soc,
            agent_connections: Vec::new(),
        };
        Agent {
            cmd_socket: send_soc,
            worker: Some(thread::spawn(move || { worker.run() }))
        }
    }
}

impl AgentService {
    pub fn new() -> AgentService {
        AgentService { agent: RefCell::new((None)) }
    }

    pub fn connect(&self, sender_did: &str, my_sk: &str, my_pk: &str, endpoint: &str, server_key: &str) -> Result<i32, CommonError> {
        let mut agent = self.agent.borrow_mut();
        if agent.is_none() {
            *agent = Some(Agent::new());
        }
        let conn_handle = SequenceUtils::get_next_id();
        let connect_cmd: AgentWorkerCommand = AgentWorkerCommand::Connect(ConnectCmd {
            did: sender_did.to_string(),
            secret_key: my_sk.to_string(),
            public_key: my_pk.to_string(),
            endpoint: endpoint.to_string(),
            server_key: server_key.to_string(),
            conn_handle: conn_handle,
        });
        agent.as_ref().unwrap().cmd_socket.send_str(connect_cmd.to_json().unwrap().as_str(), zmq::DONTWAIT).unwrap();
        Ok(conn_handle)
    }
}

impl AgentWorker {
    fn run(&mut self) {
        'agent_pool_loop: loop {
            trace!("agent worker poll loop >>");
            let cmds = self.poll().unwrap();
            for cmd in cmds {
                info!("received cmd {:?}", cmd);
                match cmd {
                    AgentWorkerCommand::Connect(cmd) => self.connect(&cmd).unwrap(),
                    AgentWorkerCommand::Response(resp) => self.agent_connections[resp.agent_ind].handle_response(resp.msg),
                    AgentWorkerCommand::Exit => break 'agent_pool_loop,
                }
            }
            trace!("agent worker poll loop <<");
        }
        trace!("agent poll finished");
    }

    fn connect(&mut self, cmd: &ConnectCmd) -> Result<(), PoolError> {
        let ra = RemoteAgent::new(cmd.public_key.as_str(), cmd.secret_key.as_str(),
                                  cmd.server_key.as_str(), cmd.endpoint.as_str(),
                                  cmd.conn_handle)
            .map_err(map_err_trace!("RemoteAgent::new failed"))?;
        ra.connect().map_err(map_err_trace!("RemoteAgent::connect failed"))?;
        self.agent_connections.push(ra);
        Ok(())
    }

    fn poll(&self) -> Result<Vec<AgentWorkerCommand>, zmq::Error> {
        let mut result = Vec::new();
        let mut poll_items: Vec<zmq::PollItem> = Vec::new();
        poll_items.push(self.cmd_socket.as_poll_item(zmq::POLLIN));

        for agent_conn in &self.agent_connections {
            poll_items.push(agent_conn.socket.as_poll_item(zmq::POLLIN));
        }

        zmq::poll(poll_items.as_mut_slice(), -1).map_err(map_err_trace!("agent poll failed"))?;

        if poll_items[0].is_readable() {
            let msg = self.cmd_socket.recv_string(zmq::DONTWAIT).unwrap().unwrap();
            info!("Input on cmd socket {}", msg);
            result.push(AgentWorkerCommand::from_json(msg.as_str()).unwrap());
        }

        for i in 0..self.agent_connections.len() {
            if poll_items[1 + i].is_readable() {
                let msg = self.agent_connections[i].socket.recv_string(zmq::DONTWAIT).unwrap().unwrap();
                info!("Input on agent socket {}: {}", i, msg);
                result.push(AgentWorkerCommand::Response(Response {
                    agent_ind: i,
                    msg: msg,
                }))
            }
        }

        Ok(result)
    }
}

impl RemoteAgent {
    fn new(pub_key: &str, sec_key: &str, ver_key: &str, addr: &str, conn_handle: i32) -> Result<RemoteAgent, PoolError> {
        Ok(RemoteAgent {
            socket: zmq::Context::new().socket(zmq::SocketType::DEALER)?,
            public_key: pub_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid pub_key {}", err))))?,
            secret_key: sec_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid sec_key {}", err))))?,
            server_key: ver_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid server_key {}", err))))?,
            addr: addr.to_string(),
            conn_handle: conn_handle,
        })
    }

    fn connect(&self) -> Result<(), PoolError> {
        impl From<zmq::EncodeError> for PoolError {
            fn from(err: zmq::EncodeError) -> PoolError {
                PoolError::CommonError(CommonError::InvalidState(format!("Invalid data stored RemoteAgent detected while connect {:?}", err)))
            }
        }
        self.socket.set_identity(zmq::z85_encode(self.public_key.as_slice())?.as_bytes())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_secretkey(zmq::z85_encode(self.secret_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_publickey(zmq::z85_encode(self.public_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_serverkey(zmq::z85_encode(self.server_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_linger(0).map_err(map_err_trace!())?; //TODO set correct timeout
        self.socket.connect(self.addr.as_str())
            .map_err(map_err_trace!("RemoteAgent::connect self.socket.connect failed"))?;
        self.socket.send_str("DID", zmq::DONTWAIT).map_err(map_err_trace!())?;
        Ok(())
    }

    fn handle_response(&self, msg: String) {
        if msg.eq("DID_ACK") {
            let send_res: Result<(), CommonError> =
                CommandExecutor::instance().send(
                    Command::Agent(
                        AgentCommand::ConnectAck(self.conn_handle, Ok(self.conn_handle))));
            if let Err(err) = send_res {
                error!("RemoteAgent::handle_response got connection ack, but can't send to client {}", err);
            };
        } else {
            //check state, transfer message to client
            unimplemented!();
        }
    }
}

#[serde(tag = "cmd")]
#[derive(Serialize, Deserialize, Debug)]
enum AgentWorkerCommand {
    Connect(ConnectCmd),
    Response(Response),
    Exit,
}

impl JsonEncodable for AgentWorkerCommand {}

impl<'a> JsonDecodable<'a> for AgentWorkerCommand {}

#[derive(Serialize, Deserialize, Debug)]
struct ConnectCmd {
    endpoint: String,
    did: String,
    secret_key: String,
    public_key: String,
    server_key: String,
    conn_handle: i32,
}

#[derive(Serialize, Deserialize, Debug)]
struct Response {
    agent_ind: usize,
    msg: String,
}

fn _create_zmq_socket_pair(address: &str, connect_and_bind: bool) -> Result<(zmq::Socket, zmq::Socket), zmq::Error> {
    let ctx = zmq::Context::new();
    let recv_soc = ctx.socket(zmq::SocketType::PAIR)?;
    let send_soc = ctx.socket(zmq::SocketType::PAIR)?;
    if connect_and_bind {
        let address = format!("inproc://{}", address);
        recv_soc.bind(&address)?;
        send_soc.connect(&address)?;
    }
    Ok((send_soc, recv_soc))
}

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

    use std::sync::mpsc::channel;
    use super::rust_base58::ToBase58;

    use utils::timeout::TimeoutUtils;

    #[test]
    fn agent_can_be_dropped() {
        let (sender, receiver) = channel();
        thread::spawn(move || {
            #[allow(unused_variables)]
            {
                let agent = Agent::new();
            }
            sender.send(true).unwrap();
        });
        receiver.recv_timeout(TimeoutUtils::short_timeout()).expect("drop not finished");
    }

    #[test]
    fn agent_service_connect_works() {
        let (sender, receiver) = channel();
        let (send_soc, recv_soc) = _create_zmq_socket_pair("test_connect", true).unwrap();
        let agent = Agent {
            cmd_socket: send_soc,
            worker: Some(thread::spawn(move || {
                sender.send(recv_soc.recv_string(0).unwrap().unwrap()).unwrap()
            }))
        };
        let agent_service = AgentService {
            agent: RefCell::new(Some(agent)),
        };
        let conn_handle = agent_service.connect("sd", "sk", "pk", "ep", "serv").unwrap();
        let expected_cmd = ConnectCmd {
            server_key: "serv".to_string(),
            public_key: "pk".to_string(),
            secret_key: "sk".to_string(),
            endpoint: "ep".to_string(),
            did: "sd".to_string(),
            conn_handle: conn_handle,
        };
        let str = receiver.recv_timeout(TimeoutUtils::short_timeout()).unwrap();
        assert_eq!(str, AgentWorkerCommand::Connect(expected_cmd).to_json().unwrap());
    }

    #[test]
    fn agent_worker_connect_works() {
        ::utils::logger::LoggerUtils::init();
        let send_key_pair = zmq::CurveKeyPair::new().unwrap();
        let recv_key_pair = zmq::CurveKeyPair::new().unwrap();
        let ctx = zmq::Context::new();
        let recv_soc = ctx.socket(zmq::SocketType::ROUTER).unwrap();
        recv_soc.set_curve_publickey(recv_key_pair.public_key.as_str()).unwrap();
        recv_soc.set_curve_secretkey(recv_key_pair.secret_key.as_str()).unwrap();
        recv_soc.set_curve_server(true).unwrap();
        recv_soc.bind("tcp://127.0.0.1:*").unwrap();
        let addr = recv_soc.get_last_endpoint().unwrap().unwrap();
        info!("addr {}", addr);

        let mut agent_worker = AgentWorker {
            agent_connections: Vec::new(),
            cmd_socket: zmq::Context::new().socket(zmq::SocketType::PAIR).unwrap(),
        };
        let cmd = ConnectCmd {
            endpoint: addr,
            public_key: zmq::z85_decode(send_key_pair.public_key.as_str()).unwrap().to_base58(),
            secret_key: zmq::z85_decode(send_key_pair.secret_key.as_str()).unwrap().to_base58(),
            did: "".to_string(),
            server_key: zmq::z85_decode(recv_key_pair.public_key.as_str()).unwrap().to_base58(),
            conn_handle: 0,
        };

        agent_worker.connect(&cmd).unwrap();

        assert_eq!(agent_worker.agent_connections.len(), 1);
        recv_soc.recv_string(0).unwrap().unwrap(); //ignore identity
        assert_eq!(recv_soc.recv_string(zmq::DONTWAIT).unwrap().unwrap(), "DID");
    }

    #[test]
    fn agent_worker_poll_works_for_cmd_socket() {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("aw_poll_cmd", true).unwrap();
        let agent_worker = AgentWorker {
            agent_connections: Vec::new(),
            cmd_socket: recv_soc,
        };
        send_soc.send_str(r#"{"cmd": "Exit"}"#, zmq::DONTWAIT).unwrap();

        let cmds = agent_worker.poll().unwrap();

        assert_eq!(cmds.len(), 1);
        assert_match!(AgentWorkerCommand::Exit, cmds[0]);
    }

    #[test]
    fn agent_worker_poll_works_for_agent_socket() {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("aw_poll_cmd", true).unwrap();
        let agent_worker = AgentWorker {
            agent_connections: vec!(RemoteAgent {
                socket: recv_soc,
                addr: String::new(),
                public_key: Vec::new(),
                secret_key: Vec::new(),
                server_key: Vec::new(),
                conn_handle: 0,
            }),
            cmd_socket: zmq::Context::new().socket(zmq::SocketType::PAIR).unwrap(),
        };
        send_soc.send_str("msg", zmq::DONTWAIT).unwrap();

        let mut cmds = agent_worker.poll().unwrap();

        assert_eq!(cmds.len(), 1);
        let cmd = cmds.remove(0);
        match cmd {
            AgentWorkerCommand::Response(resp) => {
                assert_eq!(resp.agent_ind, 0);
                assert_eq!(resp.msg, "msg");
            }
            _ => panic!("unexpected cmd {:?}", cmd),
        }
    }

    #[test]
    fn remote_agent_connect_works() {
        let dest = "test_agent_connect";
        let addr: String = format!("inproc://{}", dest);
        let (send_soc, recv_soc) = _create_zmq_socket_pair(dest, false).unwrap();
        recv_soc.bind(addr.as_str()).unwrap(); //TODO enable CurveCP
        let send_key_pair = zmq::CurveKeyPair::new().unwrap();
        let recv_key_pair = zmq::CurveKeyPair::new().unwrap();
        let agent = RemoteAgent {
            socket: send_soc,
            addr: addr,
            server_key: zmq::z85_decode(send_key_pair.public_key.as_str()).unwrap(),
            secret_key: zmq::z85_decode(recv_key_pair.secret_key.as_str()).unwrap(),
            public_key: zmq::z85_decode(recv_key_pair.public_key.as_str()).unwrap(),
            conn_handle: 0,
        };
        agent.connect().unwrap();
        assert_eq!(recv_soc.recv_string(zmq::DONTWAIT).unwrap().unwrap(), "DID");
    }

    #[test]
    fn agent_service_static_create_zmq_socket_pair_works() {
        let msg = "msg";
        let sockets = _create_zmq_socket_pair("test_pair", true).unwrap();
        sockets.0.send_str(msg, zmq::DONTWAIT).unwrap();
        assert_eq!(sockets.1.recv_string(zmq::DONTWAIT).unwrap().unwrap(), msg);
    }
}<commit_msg>Fix agent_servuce_connect_works test.<commit_after>#![warn(unused_variables)]
extern crate rust_base58;
extern crate serde_json;
extern crate zmq;

use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::thread;

use commands::{Command, CommandExecutor};
use commands::agent::AgentCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use utils::json::{JsonDecodable, JsonEncodable};
use utils::sequence::SequenceUtils;

struct RemoteAgent {
    socket: zmq::Socket,
    addr: String,
    public_key: Vec<u8>,
    secret_key: Vec<u8>,
    server_key: Vec<u8>,
    conn_handle: i32,
}

struct AgentWorker {
    cmd_socket: zmq::Socket,
    agent_connections: Vec<RemoteAgent>,
}

struct Agent {
    cmd_socket: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

impl Drop for Agent {
    fn drop(&mut self) {
        trace!("agent drop >>");
        self.cmd_socket.send_str(AgentWorkerCommand::Exit.to_json().unwrap().as_str(), zmq::DONTWAIT).unwrap(); //TODO
        self.worker.take().unwrap().join().unwrap();
        trace!("agent drop <<");
    }
}

pub struct AgentService {
    agent: RefCell<Option<Agent>>,
}

impl Agent {
    pub fn new() -> Agent {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("agent", true).unwrap();
        let mut worker = AgentWorker {
            cmd_socket: recv_soc,
            agent_connections: Vec::new(),
        };
        Agent {
            cmd_socket: send_soc,
            worker: Some(thread::spawn(move || { worker.run() }))
        }
    }
}

impl AgentService {
    pub fn new() -> AgentService {
        AgentService { agent: RefCell::new((None)) }
    }

    pub fn connect(&self, sender_did: &str, my_sk: &str, my_pk: &str, endpoint: &str, server_key: &str) -> Result<i32, CommonError> {
        let mut agent = self.agent.borrow_mut();
        if agent.is_none() {
            *agent = Some(Agent::new());
        }
        let conn_handle = SequenceUtils::get_next_id();
        let connect_cmd: AgentWorkerCommand = AgentWorkerCommand::Connect(ConnectCmd {
            did: sender_did.to_string(),
            secret_key: my_sk.to_string(),
            public_key: my_pk.to_string(),
            endpoint: endpoint.to_string(),
            server_key: server_key.to_string(),
            conn_handle: conn_handle,
        });
        agent.as_ref().unwrap().cmd_socket.send_str(connect_cmd.to_json().unwrap().as_str(), zmq::DONTWAIT).unwrap();
        Ok(conn_handle)
    }
}

impl AgentWorker {
    fn run(&mut self) {
        'agent_pool_loop: loop {
            trace!("agent worker poll loop >>");
            let cmds = self.poll().unwrap();
            for cmd in cmds {
                info!("received cmd {:?}", cmd);
                match cmd {
                    AgentWorkerCommand::Connect(cmd) => self.connect(&cmd).unwrap(),
                    AgentWorkerCommand::Response(resp) => self.agent_connections[resp.agent_ind].handle_response(resp.msg),
                    AgentWorkerCommand::Exit => break 'agent_pool_loop,
                }
            }
            trace!("agent worker poll loop <<");
        }
        trace!("agent poll finished");
    }

    fn connect(&mut self, cmd: &ConnectCmd) -> Result<(), PoolError> {
        let ra = RemoteAgent::new(cmd.public_key.as_str(), cmd.secret_key.as_str(),
                                  cmd.server_key.as_str(), cmd.endpoint.as_str(),
                                  cmd.conn_handle)
            .map_err(map_err_trace!("RemoteAgent::new failed"))?;
        ra.connect().map_err(map_err_trace!("RemoteAgent::connect failed"))?;
        self.agent_connections.push(ra);
        Ok(())
    }

    fn poll(&self) -> Result<Vec<AgentWorkerCommand>, zmq::Error> {
        let mut result = Vec::new();
        let mut poll_items: Vec<zmq::PollItem> = Vec::new();
        poll_items.push(self.cmd_socket.as_poll_item(zmq::POLLIN));

        for agent_conn in &self.agent_connections {
            poll_items.push(agent_conn.socket.as_poll_item(zmq::POLLIN));
        }

        zmq::poll(poll_items.as_mut_slice(), -1).map_err(map_err_trace!("agent poll failed"))?;

        if poll_items[0].is_readable() {
            let msg = self.cmd_socket.recv_string(zmq::DONTWAIT).unwrap().unwrap();
            info!("Input on cmd socket {}", msg);
            result.push(AgentWorkerCommand::from_json(msg.as_str()).unwrap());
        }

        for i in 0..self.agent_connections.len() {
            if poll_items[1 + i].is_readable() {
                let msg = self.agent_connections[i].socket.recv_string(zmq::DONTWAIT).unwrap().unwrap();
                info!("Input on agent socket {}: {}", i, msg);
                result.push(AgentWorkerCommand::Response(Response {
                    agent_ind: i,
                    msg: msg,
                }))
            }
        }

        Ok(result)
    }
}

impl RemoteAgent {
    fn new(pub_key: &str, sec_key: &str, ver_key: &str, addr: &str, conn_handle: i32) -> Result<RemoteAgent, PoolError> {
        Ok(RemoteAgent {
            socket: zmq::Context::new().socket(zmq::SocketType::DEALER)?,
            public_key: pub_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid pub_key {}", err))))?,
            secret_key: sec_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid sec_key {}", err))))?,
            server_key: ver_key.from_base58()
                .map_err(|err| PoolError::CommonError(CommonError::InvalidStructure(format!("invalid server_key {}", err))))?,
            addr: addr.to_string(),
            conn_handle: conn_handle,
        })
    }

    fn connect(&self) -> Result<(), PoolError> {
        impl From<zmq::EncodeError> for PoolError {
            fn from(err: zmq::EncodeError) -> PoolError {
                PoolError::CommonError(CommonError::InvalidState(format!("Invalid data stored RemoteAgent detected while connect {:?}", err)))
            }
        }
        self.socket.set_identity(zmq::z85_encode(self.public_key.as_slice())?.as_bytes())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_secretkey(zmq::z85_encode(self.secret_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_publickey(zmq::z85_encode(self.public_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_curve_serverkey(zmq::z85_encode(self.server_key.as_slice())?.as_str())
            .map_err(map_err_trace!())?;
        self.socket.set_linger(0).map_err(map_err_trace!())?; //TODO set correct timeout
        self.socket.connect(self.addr.as_str())
            .map_err(map_err_trace!("RemoteAgent::connect self.socket.connect failed"))?;
        self.socket.send_str("DID", zmq::DONTWAIT).map_err(map_err_trace!())?;
        Ok(())
    }

    fn handle_response(&self, msg: String) {
        if msg.eq("DID_ACK") {
            let send_res: Result<(), CommonError> =
                CommandExecutor::instance().send(
                    Command::Agent(
                        AgentCommand::ConnectAck(self.conn_handle, Ok(self.conn_handle))));
            if let Err(err) = send_res {
                error!("RemoteAgent::handle_response got connection ack, but can't send to client {}", err);
            };
        } else {
            //check state, transfer message to client
            unimplemented!();
        }
    }
}

#[serde(tag = "cmd")]
#[derive(Serialize, Deserialize, Debug)]
enum AgentWorkerCommand {
    Connect(ConnectCmd),
    Response(Response),
    Exit,
}

impl JsonEncodable for AgentWorkerCommand {}

impl<'a> JsonDecodable<'a> for AgentWorkerCommand {}

#[derive(Serialize, Deserialize, Debug)]
struct ConnectCmd {
    endpoint: String,
    did: String,
    secret_key: String,
    public_key: String,
    server_key: String,
    conn_handle: i32,
}

#[derive(Serialize, Deserialize, Debug)]
struct Response {
    agent_ind: usize,
    msg: String,
}

fn _create_zmq_socket_pair(address: &str, connect_and_bind: bool) -> Result<(zmq::Socket, zmq::Socket), zmq::Error> {
    let ctx = zmq::Context::new();
    let recv_soc = ctx.socket(zmq::SocketType::PAIR)?;
    let send_soc = ctx.socket(zmq::SocketType::PAIR)?;
    if connect_and_bind {
        let address = format!("inproc://{}", address);
        recv_soc.bind(&address)?;
        send_soc.connect(&address)?;
    }
    Ok((send_soc, recv_soc))
}

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

    use std::sync::mpsc::channel;
    use super::rust_base58::ToBase58;

    use utils::timeout::TimeoutUtils;

    #[test]
    fn agent_can_be_dropped() {
        let (sender, receiver) = channel();
        thread::spawn(move || {
            #[allow(unused_variables)]
            {
                let agent = Agent::new();
            }
            sender.send(true).unwrap();
        });
        receiver.recv_timeout(TimeoutUtils::short_timeout()).expect("drop not finished");
    }

    #[test]
    fn agent_service_connect_works() {
        let (sender, receiver) = channel();
        let (send_soc, recv_soc) = _create_zmq_socket_pair("test_connect", true).unwrap();
        let agent = Agent {
            cmd_socket: send_soc,
            worker: Some(thread::spawn(move || {
                sender.send(recv_soc.recv_string(0).unwrap().unwrap()).unwrap();
                recv_soc.recv_string(0).unwrap().unwrap();
            }))
        };
        let agent_service = AgentService {
            agent: RefCell::new(Some(agent)),
        };
        let conn_handle = agent_service.connect("sd", "sk", "pk", "ep", "serv").unwrap();
        let expected_cmd = ConnectCmd {
            server_key: "serv".to_string(),
            public_key: "pk".to_string(),
            secret_key: "sk".to_string(),
            endpoint: "ep".to_string(),
            did: "sd".to_string(),
            conn_handle: conn_handle,
        };
        let str = receiver.recv_timeout(TimeoutUtils::short_timeout()).unwrap();
        assert_eq!(str, AgentWorkerCommand::Connect(expected_cmd).to_json().unwrap());
    }

    #[test]
    fn agent_worker_connect_works() {
        ::utils::logger::LoggerUtils::init();
        let send_key_pair = zmq::CurveKeyPair::new().unwrap();
        let recv_key_pair = zmq::CurveKeyPair::new().unwrap();
        let ctx = zmq::Context::new();
        let recv_soc = ctx.socket(zmq::SocketType::ROUTER).unwrap();
        recv_soc.set_curve_publickey(recv_key_pair.public_key.as_str()).unwrap();
        recv_soc.set_curve_secretkey(recv_key_pair.secret_key.as_str()).unwrap();
        recv_soc.set_curve_server(true).unwrap();
        recv_soc.bind("tcp://127.0.0.1:*").unwrap();
        let addr = recv_soc.get_last_endpoint().unwrap().unwrap();
        info!("addr {}", addr);

        let mut agent_worker = AgentWorker {
            agent_connections: Vec::new(),
            cmd_socket: zmq::Context::new().socket(zmq::SocketType::PAIR).unwrap(),
        };
        let cmd = ConnectCmd {
            endpoint: addr,
            public_key: zmq::z85_decode(send_key_pair.public_key.as_str()).unwrap().to_base58(),
            secret_key: zmq::z85_decode(send_key_pair.secret_key.as_str()).unwrap().to_base58(),
            did: "".to_string(),
            server_key: zmq::z85_decode(recv_key_pair.public_key.as_str()).unwrap().to_base58(),
            conn_handle: 0,
        };

        agent_worker.connect(&cmd).unwrap();

        assert_eq!(agent_worker.agent_connections.len(), 1);
        recv_soc.recv_string(0).unwrap().unwrap(); //ignore identity
        assert_eq!(recv_soc.recv_string(zmq::DONTWAIT).unwrap().unwrap(), "DID");
    }

    #[test]
    fn agent_worker_poll_works_for_cmd_socket() {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("aw_poll_cmd", true).unwrap();
        let agent_worker = AgentWorker {
            agent_connections: Vec::new(),
            cmd_socket: recv_soc,
        };
        send_soc.send_str(r#"{"cmd": "Exit"}"#, zmq::DONTWAIT).unwrap();

        let cmds = agent_worker.poll().unwrap();

        assert_eq!(cmds.len(), 1);
        assert_match!(AgentWorkerCommand::Exit, cmds[0]);
    }

    #[test]
    fn agent_worker_poll_works_for_agent_socket() {
        let (send_soc, recv_soc) = _create_zmq_socket_pair("aw_poll_cmd", true).unwrap();
        let agent_worker = AgentWorker {
            agent_connections: vec!(RemoteAgent {
                socket: recv_soc,
                addr: String::new(),
                public_key: Vec::new(),
                secret_key: Vec::new(),
                server_key: Vec::new(),
                conn_handle: 0,
            }),
            cmd_socket: zmq::Context::new().socket(zmq::SocketType::PAIR).unwrap(),
        };
        send_soc.send_str("msg", zmq::DONTWAIT).unwrap();

        let mut cmds = agent_worker.poll().unwrap();

        assert_eq!(cmds.len(), 1);
        let cmd = cmds.remove(0);
        match cmd {
            AgentWorkerCommand::Response(resp) => {
                assert_eq!(resp.agent_ind, 0);
                assert_eq!(resp.msg, "msg");
            }
            _ => panic!("unexpected cmd {:?}", cmd),
        }
    }

    #[test]
    fn remote_agent_connect_works() {
        let dest = "test_agent_connect";
        let addr: String = format!("inproc://{}", dest);
        let (send_soc, recv_soc) = _create_zmq_socket_pair(dest, false).unwrap();
        recv_soc.bind(addr.as_str()).unwrap(); //TODO enable CurveCP
        let send_key_pair = zmq::CurveKeyPair::new().unwrap();
        let recv_key_pair = zmq::CurveKeyPair::new().unwrap();
        let agent = RemoteAgent {
            socket: send_soc,
            addr: addr,
            server_key: zmq::z85_decode(send_key_pair.public_key.as_str()).unwrap(),
            secret_key: zmq::z85_decode(recv_key_pair.secret_key.as_str()).unwrap(),
            public_key: zmq::z85_decode(recv_key_pair.public_key.as_str()).unwrap(),
            conn_handle: 0,
        };
        agent.connect().unwrap();
        assert_eq!(recv_soc.recv_string(zmq::DONTWAIT).unwrap().unwrap(), "DID");
    }

    #[test]
    fn agent_service_static_create_zmq_socket_pair_works() {
        let msg = "msg";
        let sockets = _create_zmq_socket_pair("test_pair", true).unwrap();
        sockets.0.send_str(msg, zmq::DONTWAIT).unwrap();
        assert_eq!(sockets.1.recv_string(zmq::DONTWAIT).unwrap().unwrap(), msg);
    }
}
<|endoftext|>


<commit_before>export make_url;

import std::net::url;
import url::{get_scheme, url};

/**
Create a URL object from a string. Does various helpful browsery things like

* If there's no current url and the path looks like a file then it will
  create a file url based of the current working directory
* If there's a current url and the new path is relative then the new url
  is based off the current url

*/
fn make_url(str_url: ~str, current_url: option<url>) -> url {
    let str_url = if get_scheme(str_url).is_some() {
        str_url
    } else {
        if current_url.is_none() {
            // If all we have is a filename, assume it's a local relative file
            // and build an absolute path with the cwd
            ~"file://" + path::connect(os::getcwd(), str_url)
        } else {
            let current_url = current_url.get();
            #debug("make_url: current_url: %?", current_url);
            if current_url.path.is_empty() || current_url.path.ends_with("/") {
                current_url.scheme + "://" + path::connect(current_url.host, str_url)
            } else {
                let path = path::split(current_url.path);
                let path = path.init();
                let path = path::connect_many(path + ~[str_url]);

                current_url.scheme + "://" + path::connect(current_url.host, path)
            }
        }
    };

    // FIXME: Need to handle errors
    url::from_str(str_url).get()
}

#[test]
fn should_create_absolute_file_url_if_current_url_is_none_and_str_url_looks_filey() {
    let file = ~"local.html";
    let url = make_url(file, none);
    #debug("url: %?", url);
    assert url.scheme == ~"file";
    assert url.path.contains(os::getcwd());
}

#[test]
fn should_create_url_based_on_old_url_1() {
    let old_str = ~"http://example.com";
    let old_url = make_url(old_str, none);
    let new_str = ~"index.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/index.html";
}

#[test]
fn should_create_url_based_on_old_url_2() {
    let old_str = ~"http://example.com/";
    let old_url = make_url(old_str, none);
    let new_str = ~"index.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/index.html";
}

#[test]
fn should_create_url_based_on_old_url_3() {
    let old_str = ~"http://example.com/index.html";
    let old_url = make_url(old_str, none);
    let new_str = ~"crumpet.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/crumpet.html";
}

#[test]
fn should_create_url_based_on_old_url_4() {
    let old_str = ~"http://example.com/snarf/index.html";
    let old_url = make_url(old_str, none);
    let new_str = ~"crumpet.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/snarf/crumpet.html";
}
<commit_msg>Adapt to net_url changes<commit_after>export make_url;

import std::net::url;
import url::{get_scheme, url};

/**
Create a URL object from a string. Does various helpful browsery things like

* If there's no current url and the path looks like a file then it will
  create a file url based of the current working directory
* If there's a current url and the new path is relative then the new url
  is based off the current url

*/
fn make_url(str_url: ~str, current_url: option<url>) -> url {
    let mut schm = get_scheme(str_url);
    let str_url = if result::is_err(schm) {
        if current_url.is_none() {
            // If all we have is a filename, assume it's a local relative file
            // and build an absolute path with the cwd
            ~"file://" + path::connect(os::getcwd(), str_url)
        } else {
            let current_url = current_url.get();
            #debug("make_url: current_url: %?", current_url);
            if current_url.path.is_empty() || current_url.path.ends_with("/") {
                current_url.scheme + "://" + path::connect(current_url.host, str_url)
            } else {
                let path = path::split(current_url.path);
                let path = path.init();
                let path = path::connect_many(path + ~[str_url]);

                current_url.scheme + "://" + path::connect(current_url.host, path)
            }
        }
    } else {
        str_url
    };

    // FIXME: Need to handle errors
    url::from_str(str_url).get()
}

#[test]
fn should_create_absolute_file_url_if_current_url_is_none_and_str_url_looks_filey() {
    let file = ~"local.html";
    let url = make_url(file, none);
    #debug("url: %?", url);
    assert url.scheme == ~"file";
    assert url.path.contains(os::getcwd());
}

#[test]
fn should_create_url_based_on_old_url_1() {
    let old_str = ~"http://example.com";
    let old_url = make_url(old_str, none);
    let new_str = ~"index.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/index.html";
}

#[test]
fn should_create_url_based_on_old_url_2() {
    let old_str = ~"http://example.com/";
    let old_url = make_url(old_str, none);
    let new_str = ~"index.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/index.html";
}

#[test]
fn should_create_url_based_on_old_url_3() {
    let old_str = ~"http://example.com/index.html";
    let old_url = make_url(old_str, none);
    let new_str = ~"crumpet.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/crumpet.html";
}

#[test]
fn should_create_url_based_on_old_url_4() {
    let old_str = ~"http://example.com/snarf/index.html";
    let old_url = make_url(old_str, none);
    let new_str = ~"crumpet.html";
    let new_url = make_url(new_str, some(old_url));
    assert new_url.scheme == ~"http";
    assert new_url.host == ~"example.com";
    assert new_url.path == ~"/snarf/crumpet.html";
}
<|endoftext|>


<commit_before>use regex::RegexSet;
use slog::Logger;
use std::fs::canonicalize;
use std::path::Path;
use std::process::exit;

pub fn resolve_path(dir: &str) -> Option<String> {
    match canonicalize(Path::new(dir)) {
        Ok(p) => Some(p.to_string_lossy().into_owned()),
        Err(_) => None,
    }
}

pub fn print_error_and_exit(msg: &str) {
    println!("{}", msg);
    exit(1);
}

pub fn log_error_and_exit(log: &Logger, msg: &str) {
    error!(log, msg);
    exit(1);
}

pub fn process_ignores(vec: &mut Vec<String>) -> RegexSet {
    if vec.len() == 0 {
        vec.push("^\\.git(?:/[^/]+)*$".to_owned());
    }
    vec.push("^\\.bindrs.*$".to_owned());

    vec_to_regex_set(&vec)
}

fn vec_to_regex_set(ignores: &Vec<String>) -> RegexSet {
    let mut regexes: Vec<String> = vec![];

    for i in ignores.iter() {
        let mut ignore = i.clone();
        if !(ignore.starts_with("^") && ignore.ends_with("$")) {
            ignore = format!("^{}(?:/[^/]+)*$", ignore);
        }
        regexes.push(ignore)
    }

    RegexSet::new(&regexes[..]).unwrap()
}
<commit_msg>Wait between error! and exit<commit_after>use regex::RegexSet;
use slog::Logger;
use std::{thread, time};
use std::fs::canonicalize;
use std::path::Path;
use std::process::exit;

pub fn resolve_path(dir: &str) -> Option<String> {
    match canonicalize(Path::new(dir)) {
        Ok(p) => Some(p.to_string_lossy().into_owned()),
        Err(_) => None,
    }
}

pub fn print_error_and_exit(msg: &str) {
    println!("{}", msg);
    exit(1);
}

pub fn log_error_and_exit(log: &Logger, msg: &str) {
    error!(log, msg);
    thread::sleep(time::Duration::from_millis(500));
    exit(1);
}

pub fn process_ignores(vec: &mut Vec<String>) -> RegexSet {
    if vec.len() == 0 {
        vec.push("^\\.git(?:/[^/]+)*$".to_owned());
    }
    vec.push("^\\.bindrs.*$".to_owned());

    vec_to_regex_set(&vec)
}

fn vec_to_regex_set(ignores: &Vec<String>) -> RegexSet {
    let mut regexes: Vec<String> = vec![];

    for i in ignores.iter() {
        let mut ignore = i.clone();
        if !(ignore.starts_with("^") && ignore.ends_with("$")) {
            ignore = format!("^{}(?:/[^/]+)*$", ignore);
        }
        regexes.push(ignore)
    }

    RegexSet::new(&regexes[..]).unwrap()
}
<|endoftext|>


<commit_before>//! Backend using the pure-rust crossplatform crossterm library.
//!
//! Requires the `crossterm-backend` feature.

#![cfg(feature = "crossterm")]

use crate::vec::Vec2;
use crate::{backend, theme};
use crossterm::{
    cursor, execute, input, queue, terminal, AlternateScreen, AsyncReader,
    Attribute, Clear, ClearType, Color, Goto, InputEvent as CInputEvent,
    KeyEvent as CKeyEvent, MouseButton as CMouseButton,
    MouseEvent as CMouseEvent, SetAttr, SetBg, SetFg, Show, Terminal,
};

use crate::event::{Event, Key, MouseButton, MouseEvent};
use std::cell::{Cell, RefCell};
use std::io::{self, BufWriter, Stdout, Write};

/// Backend using crossterm
pub struct Backend {
    current_style: Cell<theme::ColorPair>,
    last_button: Option<MouseButton>,
    // reader to read user input async.
    async_reader: AsyncReader,
    _alternate_screen: AlternateScreen,
    stdout: RefCell<BufWriter<Stdout>>,
    terminal: Terminal,
}

impl Backend {
    /// Creates a new crossterm backend.
    pub fn init() -> Result<Box<dyn backend::Backend>, crossterm::ErrorKind>
    where
        Self: Sized,
    {
        let _alternate_screen = AlternateScreen::to_alternate(true)?;

        let input = input();
        let async_reader = input.read_async();
        input.enable_mouse_mode().unwrap();

        cursor().hide()?;

        Ok(Box::new(Backend {
            current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
            last_button: None,
            async_reader,
            _alternate_screen,
            stdout: RefCell::new(BufWriter::new(io::stdout())),
            terminal: terminal(),
        }))
    }

    fn apply_colors(&self, colors: theme::ColorPair) {
        with_color(colors.front, |c| {
            queue!(self.stdout.borrow_mut(), SetFg(*c))
        })
        .unwrap();
        with_color(colors.back, |c| {
            queue!(self.stdout.borrow_mut(), SetBg(*c))
        })
        .unwrap();
    }

    fn write<T>(&self, content: T)
    where
        T: std::fmt::Display,
    {
        write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
            .unwrap();
    }

    fn set_attr(&self, attr: Attribute) {
        queue!(self.stdout.borrow_mut(), SetAttr(attr)).unwrap();
    }

    fn map_key(&mut self, event: CInputEvent) -> Event {
        match event {
            CInputEvent::Keyboard(key_event) => match key_event {
                CKeyEvent::Esc => Event::Key(Key::Esc),
                CKeyEvent::Backspace => Event::Key(Key::Backspace),
                CKeyEvent::Left => Event::Key(Key::Left),
                CKeyEvent::Right => Event::Key(Key::Right),
                CKeyEvent::Up => Event::Key(Key::Up),
                CKeyEvent::Down => Event::Key(Key::Down),
                CKeyEvent::Home => Event::Key(Key::Home),
                CKeyEvent::End => Event::Key(Key::End),
                CKeyEvent::PageUp => Event::Key(Key::PageUp),
                CKeyEvent::PageDown => Event::Key(Key::PageDown),
                CKeyEvent::Delete => Event::Key(Key::Del),
                CKeyEvent::Insert => Event::Key(Key::Ins),
                CKeyEvent::Enter => Event::Key(Key::Enter),
                CKeyEvent::Tab => Event::Key(Key::Tab),
                CKeyEvent::F(n) => Event::Key(Key::from_f(n)),
                CKeyEvent::Char(c) => Event::Char(c),
                CKeyEvent::Ctrl('c') => Event::Exit,
                CKeyEvent::Ctrl(c) => Event::CtrlChar(c),
                CKeyEvent::Alt(c) => Event::AltChar(c),
                _ => Event::Unknown(vec![]),
            },
            CInputEvent::Mouse(mouse_event) => match mouse_event {
                CMouseEvent::Press(btn, x, y) => {
                    let position = (x - 1, y - 1).into();

                    let event = match btn {
                        CMouseButton::Left => {
                            MouseEvent::Press(MouseButton::Left)
                        }
                        CMouseButton::Middle => {
                            MouseEvent::Press(MouseButton::Middle)
                        }
                        CMouseButton::Right => {
                            MouseEvent::Press(MouseButton::Right)
                        }
                        CMouseButton::WheelUp => MouseEvent::WheelUp,
                        CMouseButton::WheelDown => MouseEvent::WheelDown,
                    };

                    if let MouseEvent::Press(btn) = event {
                        self.last_button = Some(btn);
                    }

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Release(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Release(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Hold(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Hold(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                _ => {
                    log::warn!(
                        "Unknown mouse button event {:?}!",
                        mouse_event
                    );
                    Event::Unknown(vec![])
                }
            },
            _ => {
                log::warn!("Unknown mouse event {:?}!", event);
                Event::Unknown(vec![])
            }
        }
    }
}

impl backend::Backend for Backend {
    fn name(&self) -> &str {
        "crossterm"
    }

    fn poll_event(&mut self) -> Option<Event> {
        self.async_reader.next().map(|event| self.map_key(event))
    }

    fn finish(&mut self) {
        execute!(
            self.stdout.borrow_mut(),
            Goto(0, 0),
            Clear(ClearType::All),
            SetBg(Color::Reset),
            SetFg(Color::Reset),
            SetAttr(Attribute::Reset),
            Show
        )
        .unwrap();
        input().disable_mouse_mode().unwrap();
    }

    fn refresh(&mut self) {
        self.stdout.borrow_mut().flush().unwrap();
    }

    fn has_colors(&self) -> bool {
        // TODO: color support detection?
        true
    }

    fn screen_size(&self) -> Vec2 {
        let size = self.terminal.size().unwrap_or((0, 0));
        Vec2::from(size)
    }

    fn print_at(&self, pos: Vec2, text: &str) {
        queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
            .unwrap();
        self.write(text);
    }

    fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
        if repetitions > 0 {
            let mut out = self.stdout.borrow_mut();

            queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();

            // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
            // Current work aground is writing bytes instead of a string to the terminal.
            out.write_all(text.as_bytes()).unwrap();

            let mut dupes_left = repetitions - 1;
            while dupes_left > 0 {
                out.write_all(text.as_bytes()).unwrap();
                dupes_left -= 1;
            }
        }
    }

    fn clear(&self, color: theme::Color) {
        self.apply_colors(theme::ColorPair {
            front: color,
            back: color,
        });

        queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
    }

    fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
        let current_style = self.current_style.get();

        if current_style != color {
            self.apply_colors(color);
            self.current_style.set(color);
        }

        current_style
    }

    fn set_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::Bold),
            theme::Effect::Italic => self.set_attr(Attribute::Italic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::CrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }

    fn unset_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::NoBold),
            theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::NotCrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }
}

fn with_color<F, R>(clr: theme::Color, f: F) -> R
where
    F: FnOnce(&Color) -> R,
{
    match clr {
        theme::Color::Dark(theme::BaseColor::Black) => f(&Color::Black),
        theme::Color::Dark(theme::BaseColor::Red) => f(&Color::DarkRed),
        theme::Color::Dark(theme::BaseColor::Green) => f(&Color::DarkGreen),
        theme::Color::Dark(theme::BaseColor::Yellow) => f(&Color::DarkYellow),
        theme::Color::Dark(theme::BaseColor::Blue) => f(&Color::DarkBlue),
        theme::Color::Dark(theme::BaseColor::Magenta) => {
            f(&Color::DarkMagenta)
        }
        theme::Color::Dark(theme::BaseColor::Cyan) => f(&Color::DarkCyan),
        theme::Color::Dark(theme::BaseColor::White) => f(&Color::Grey),

        theme::Color::Light(theme::BaseColor::Black) => f(&Color::Grey),
        theme::Color::Light(theme::BaseColor::Red) => f(&Color::Red),
        theme::Color::Light(theme::BaseColor::Green) => f(&Color::Green),
        theme::Color::Light(theme::BaseColor::Yellow) => f(&Color::Yellow),
        theme::Color::Light(theme::BaseColor::Blue) => f(&Color::Blue),
        theme::Color::Light(theme::BaseColor::Magenta) => f(&Color::Magenta),
        theme::Color::Light(theme::BaseColor::Cyan) => f(&Color::Cyan),
        theme::Color::Light(theme::BaseColor::White) => f(&Color::White),

        theme::Color::Rgb(r, g, b) => f(&Color::Rgb { r, g, b }),
        theme::Color::RgbLowRes(r, g, b) => {
            debug_assert!(r <= 5,
                          "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
                          r);
            debug_assert!(g <= 5,
                          "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
                          g);
            debug_assert!(b <= 5,
                          "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
                          b);

            f(&Color::AnsiValue(16 + 36 * r + 6 * g + b))
        }

        theme::Color::TerminalDefault => {
            unimplemented!(
                "I have to take a look at how reset has to work out"
            );
        }
    }
}
<commit_msg>crossterm: Stop mouse mode and show cursor at finish (#400)<commit_after>//! Backend using the pure-rust crossplatform crossterm library.
//!
//! Requires the `crossterm-backend` feature.

#![cfg(feature = "crossterm")]

use crate::vec::Vec2;
use crate::{backend, theme};
use crossterm::{
    cursor, input, queue, terminal, AlternateScreen, AsyncReader, Attribute,
    Clear, ClearType, Color, Goto, InputEvent as CInputEvent,
    KeyEvent as CKeyEvent, MouseButton as CMouseButton,
    MouseEvent as CMouseEvent, SetAttr, SetBg, SetFg, Terminal,
};

use crate::event::{Event, Key, MouseButton, MouseEvent};
use std::cell::{Cell, RefCell};
use std::io::{self, BufWriter, Stdout, Write};

/// Backend using crossterm
pub struct Backend {
    current_style: Cell<theme::ColorPair>,
    last_button: Option<MouseButton>,
    // reader to read user input async.
    async_reader: AsyncReader,
    _alternate_screen: AlternateScreen,
    stdout: RefCell<BufWriter<Stdout>>,
    terminal: Terminal,
}

impl Backend {
    /// Creates a new crossterm backend.
    pub fn init() -> Result<Box<dyn backend::Backend>, crossterm::ErrorKind>
    where
        Self: Sized,
    {
        let _alternate_screen = AlternateScreen::to_alternate(true)?;

        let input = input();
        let async_reader = input.read_async();
        input.enable_mouse_mode().unwrap();

        cursor().hide()?;

        Ok(Box::new(Backend {
            current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
            last_button: None,
            async_reader,
            _alternate_screen,
            stdout: RefCell::new(BufWriter::new(io::stdout())),
            terminal: terminal(),
        }))
    }

    fn apply_colors(&self, colors: theme::ColorPair) {
        with_color(colors.front, |c| {
            queue!(self.stdout.borrow_mut(), SetFg(*c))
        })
        .unwrap();
        with_color(colors.back, |c| {
            queue!(self.stdout.borrow_mut(), SetBg(*c))
        })
        .unwrap();
    }

    fn write<T>(&self, content: T)
    where
        T: std::fmt::Display,
    {
        write!(self.stdout.borrow_mut(), "{}", format!("{}", content))
            .unwrap();
    }

    fn set_attr(&self, attr: Attribute) {
        queue!(self.stdout.borrow_mut(), SetAttr(attr)).unwrap();
    }

    fn map_key(&mut self, event: CInputEvent) -> Event {
        match event {
            CInputEvent::Keyboard(key_event) => match key_event {
                CKeyEvent::Esc => Event::Key(Key::Esc),
                CKeyEvent::Backspace => Event::Key(Key::Backspace),
                CKeyEvent::Left => Event::Key(Key::Left),
                CKeyEvent::Right => Event::Key(Key::Right),
                CKeyEvent::Up => Event::Key(Key::Up),
                CKeyEvent::Down => Event::Key(Key::Down),
                CKeyEvent::Home => Event::Key(Key::Home),
                CKeyEvent::End => Event::Key(Key::End),
                CKeyEvent::PageUp => Event::Key(Key::PageUp),
                CKeyEvent::PageDown => Event::Key(Key::PageDown),
                CKeyEvent::Delete => Event::Key(Key::Del),
                CKeyEvent::Insert => Event::Key(Key::Ins),
                CKeyEvent::Enter => Event::Key(Key::Enter),
                CKeyEvent::Tab => Event::Key(Key::Tab),
                CKeyEvent::F(n) => Event::Key(Key::from_f(n)),
                CKeyEvent::Char(c) => Event::Char(c),
                CKeyEvent::Ctrl('c') => Event::Exit,
                CKeyEvent::Ctrl(c) => Event::CtrlChar(c),
                CKeyEvent::Alt(c) => Event::AltChar(c),
                _ => Event::Unknown(vec![]),
            },
            CInputEvent::Mouse(mouse_event) => match mouse_event {
                CMouseEvent::Press(btn, x, y) => {
                    let position = (x - 1, y - 1).into();

                    let event = match btn {
                        CMouseButton::Left => {
                            MouseEvent::Press(MouseButton::Left)
                        }
                        CMouseButton::Middle => {
                            MouseEvent::Press(MouseButton::Middle)
                        }
                        CMouseButton::Right => {
                            MouseEvent::Press(MouseButton::Right)
                        }
                        CMouseButton::WheelUp => MouseEvent::WheelUp,
                        CMouseButton::WheelDown => MouseEvent::WheelDown,
                    };

                    if let MouseEvent::Press(btn) = event {
                        self.last_button = Some(btn);
                    }

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Release(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Release(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                CMouseEvent::Hold(x, y) if self.last_button.is_some() => {
                    let event = MouseEvent::Hold(self.last_button.unwrap());
                    let position = (x - 1, y - 1).into();

                    Event::Mouse {
                        event,
                        position,
                        offset: Vec2::zero(),
                    }
                }
                _ => {
                    log::warn!(
                        "Unknown mouse button event {:?}!",
                        mouse_event
                    );
                    Event::Unknown(vec![])
                }
            },
            _ => {
                log::warn!("Unknown mouse event {:?}!", event);
                Event::Unknown(vec![])
            }
        }
    }
}

impl backend::Backend for Backend {
    fn name(&self) -> &str {
        "crossterm"
    }

    fn poll_event(&mut self) -> Option<Event> {
        self.async_reader.next().map(|event| self.map_key(event))
    }

    fn finish(&mut self) {
        input().disable_mouse_mode().unwrap();
        cursor().show().unwrap();
    }

    fn refresh(&mut self) {
        self.stdout.borrow_mut().flush().unwrap();
    }

    fn has_colors(&self) -> bool {
        // TODO: color support detection?
        true
    }

    fn screen_size(&self) -> Vec2 {
        let size = self.terminal.size().unwrap_or((0, 0));
        Vec2::from(size)
    }

    fn print_at(&self, pos: Vec2, text: &str) {
        queue!(self.stdout.borrow_mut(), Goto(pos.x as u16, pos.y as u16))
            .unwrap();
        self.write(text);
    }

    fn print_at_rep(&self, pos: Vec2, repetitions: usize, text: &str) {
        if repetitions > 0 {
            let mut out = self.stdout.borrow_mut();

            queue!(out, Goto(pos.x as u16, pos.y as u16)).unwrap();

            // as I (Timon) wrote this I figured out that calling `write_str` for unix was flushing the stdout.
            // Current work aground is writing bytes instead of a string to the terminal.
            out.write_all(text.as_bytes()).unwrap();

            let mut dupes_left = repetitions - 1;
            while dupes_left > 0 {
                out.write_all(text.as_bytes()).unwrap();
                dupes_left -= 1;
            }
        }
    }

    fn clear(&self, color: theme::Color) {
        self.apply_colors(theme::ColorPair {
            front: color,
            back: color,
        });

        queue!(self.stdout.borrow_mut(), Clear(ClearType::All)).unwrap();
    }

    fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
        let current_style = self.current_style.get();

        if current_style != color {
            self.apply_colors(color);
            self.current_style.set(color);
        }

        current_style
    }

    fn set_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::Bold),
            theme::Effect::Italic => self.set_attr(Attribute::Italic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::CrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }

    fn unset_effect(&self, effect: theme::Effect) {
        match effect {
            theme::Effect::Simple => (),
            theme::Effect::Reverse => self.set_attr(Attribute::Reverse),
            theme::Effect::Bold => self.set_attr(Attribute::NoBold),
            theme::Effect::Italic => self.set_attr(Attribute::NoItalic),
            theme::Effect::Strikethrough => {
                self.set_attr(Attribute::NotCrossedOut)
            }
            theme::Effect::Underline => self.set_attr(Attribute::Underlined),
        }
    }
}

fn with_color<F, R>(clr: theme::Color, f: F) -> R
where
    F: FnOnce(&Color) -> R,
{
    match clr {
        theme::Color::Dark(theme::BaseColor::Black) => f(&Color::Black),
        theme::Color::Dark(theme::BaseColor::Red) => f(&Color::DarkRed),
        theme::Color::Dark(theme::BaseColor::Green) => f(&Color::DarkGreen),
        theme::Color::Dark(theme::BaseColor::Yellow) => f(&Color::DarkYellow),
        theme::Color::Dark(theme::BaseColor::Blue) => f(&Color::DarkBlue),
        theme::Color::Dark(theme::BaseColor::Magenta) => {
            f(&Color::DarkMagenta)
        }
        theme::Color::Dark(theme::BaseColor::Cyan) => f(&Color::DarkCyan),
        theme::Color::Dark(theme::BaseColor::White) => f(&Color::Grey),

        theme::Color::Light(theme::BaseColor::Black) => f(&Color::Grey),
        theme::Color::Light(theme::BaseColor::Red) => f(&Color::Red),
        theme::Color::Light(theme::BaseColor::Green) => f(&Color::Green),
        theme::Color::Light(theme::BaseColor::Yellow) => f(&Color::Yellow),
        theme::Color::Light(theme::BaseColor::Blue) => f(&Color::Blue),
        theme::Color::Light(theme::BaseColor::Magenta) => f(&Color::Magenta),
        theme::Color::Light(theme::BaseColor::Cyan) => f(&Color::Cyan),
        theme::Color::Light(theme::BaseColor::White) => f(&Color::White),

        theme::Color::Rgb(r, g, b) => f(&Color::Rgb { r, g, b }),
        theme::Color::RgbLowRes(r, g, b) => {
            debug_assert!(r <= 5,
                          "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
                          r);
            debug_assert!(g <= 5,
                          "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
                          g);
            debug_assert!(b <= 5,
                          "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
                          b);

            f(&Color::AnsiValue(16 + 36 * r + 6 * g + b))
        }

        theme::Color::TerminalDefault => {
            unimplemented!(
                "I have to take a look at how reset has to work out"
            );
        }
    }
}
<|endoftext|>