<commit_before>/// low level wrapper for the state machine, encoding and decoding from lapin-async
use lapin_async::connection::*;
use lapin_async::format::frame::*;

use nom::{IResult,Offset};
use cookie_factory::GenError;
use bytes::BytesMut;
use std::cmp;
use std::iter::repeat;
use std::io::{self,Error,ErrorKind};
use std::sync::{Arc,Mutex};
use std::thread;
use std::time::Duration;
use futures::{Async,Poll,Sink,Stream,StartSend,Future,future};
use tokio_core::reactor::Core;
use tokio_io::{AsyncRead,AsyncWrite};
use tokio_io::codec::{Decoder,Encoder,Framed};
use tokio_timer::Timer;
use channel::BasicProperties;
use client::ConnectionOptions;

/// implements tokio-io's Decoder and Encoder
pub struct AMQPCodec {
    pub frame_max: u32,
}

impl Decoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> {
        let (consumed, f) = match frame(buf) {
          IResult::Incomplete(_) => {
            return Ok(None)
          },
          IResult::Error(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("parse error: {:?}", e)))
          },
          IResult::Done(i, frame) => {
            (buf.offset(i), frame)
          }
        };

        trace!("decoded frame: {:?}", f);

        buf.split_to(consumed);

        Ok(Some(f))
    }
}

impl Encoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, frame: Frame, buf: &mut BytesMut) -> Result<(), Self::Error> {
      let length    = buf.len();
      // Ensure we at least allocate 8192 so that the buffer is big enough for the frame_max
      // negociation. Afterwards, use frame_max if > 8192.
      let frame_max = cmp::max(self.frame_max, 8192) as usize;
      if length < frame_max {
        //reserve more capacity and intialize it
        buf.extend(repeat(0).take(frame_max - length));
      }
      trace!("will encode and write frame: {:?}", frame);

      loop {
        let gen_res = match &frame {
          &Frame::ProtocolHeader => {
            gen_protocol_header((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Heartbeat(_) => {
            gen_heartbeat_frame((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Method(channel, ref method) => {
            gen_method_frame((buf, 0), channel, method).map(|tup| tup.1)
          },
          &Frame::Header(channel_id, class_id, ref header) => {
            gen_content_header_frame((buf, 0), channel_id, class_id, header.body_size, &header.properties).map(|tup| tup.1)
          },
          &Frame::Body(channel_id, ref data) => {
            gen_content_body_frame((buf, 0), channel_id, data).map(|tup| tup.1)
          }
        };

        match gen_res {
          Ok(sz) => {
            buf.truncate(sz);
            trace!("serialized frame: {} bytes", sz);
            return Ok(());
          },
          Err(e) => {
            error!("error generating frame: {:?}", e);
            match e {
              GenError::BufferTooSmall(sz) => {
                buf.extend(repeat(0).take(sz - length));
                //return Err(Error::new(ErrorKind::InvalidData, "send buffer too small"));
              },
              GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
                return Err(Error::new(ErrorKind::InvalidData, "could not generate"));
              }
            }
          }
        }
      }
    }
}

/// Wrappers over a `Framed` stream using `AMQPCodec` and lapin-async's `Connection`
pub struct AMQPTransport<T> {
  upstream: Framed<T,AMQPCodec>,
  pub conn: Connection,
}

impl<T> AMQPTransport<T>
   where T: AsyncRead+AsyncWrite,
         T: Sync+Send,
         T: 'static               {

  /// starts the connection process
  ///
  /// returns a future of a `AMQPTransport` that is connected
  pub fn connect(stream: T, options: &ConnectionOptions) -> Box<Future<Item = AMQPTransport<T>, Error = io::Error>> {
    let mut conn = Connection::new();
    conn.set_credentials(&options.username, &options.password);
    conn.set_vhost(&options.vhost);
    conn.set_frame_max(options.frame_max);
    conn.set_heartbeat(options.heartbeat);
    if let Err(e) = conn.connect() {
      let err = format!("Failed to connect: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let codec = AMQPCodec {
      frame_max: conn.configuration.frame_max,
    };
    let mut t = AMQPTransport {
      upstream:  stream.framed(codec),
      conn:      conn,
    };

    if let Err(e) = t.send_and_handle_frames() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let mut connector = AMQPTransportConnector {
      transport: Some(t),
    };

    trace!("pre-poll");
    if let Err(e) = connector.poll() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }
    trace!("post-poll");

    Box::new(connector)
  }

  pub fn start_heartbeat(&self, transport: Arc<Mutex<AMQPTransport<T>>>) -> io::Result<()> {
      let heartbeat = self.conn.configuration.heartbeat as u64;
      if heartbeat > 0 {
          thread::Builder::new().name("lapin heartbeat".to_string()).spawn(move || {
              Core::new().unwrap().run(Timer::default().interval(Duration::from_secs(heartbeat)).map_err(From::from).for_each(|_| {
                  debug!("poll heartbeat");
                  if let Ok(mut transport) = transport.lock() {
                      debug!("Sending heartbeat");
                      if let Err(e) = transport.send_frame(Frame::Heartbeat(0)) {
                          error!("Failed to send heartbeat: {:?}", e);
                          return Err(e);
                      } else {
                          Ok(())
                      }
                  } else {
                      Ok(())
                  }
              })).unwrap()
          })?;
      }
      Ok(())
  }

  fn poll_upstream(&mut self) -> Poll<Option<()>, io::Error> {
    trace!("poll upstream");
    let value = match self.upstream.poll() {
      Ok(Async::Ready(t)) => t,
      Ok(Async::NotReady) => {
        trace!("upstream poll gave NotReady");
        return Ok(Async::NotReady);
      },
      Err(e) => {
        error!("upstream poll gave error: {:?}", e);
        return Err(From::from(e));
      },
    };

    if let Some(frame) = value {
      trace!("upstream poll gave frame: {:?}", frame);
      if let Err(e) = self.conn.handle_frame(frame) {
        let err = format!("failed to handle frame: {:?}", e);
        return Err(io::Error::new(io::ErrorKind::Other, err));
      }
      self.send_frames()?;
      Ok(Async::Ready(Some(())))
    } else {
      error!("upstream poll gave Ready(None)");
      Ok(Async::Ready(None))
    }
  }

  pub fn send_and_handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    self.send_frames()?;
    self.handle_frames()
  }

  pub fn send_and_handle_frames_with_payload(&mut self, channel_id: u16, payload: &[u8], properties: BasicProperties) -> Poll<Option<()>, io::Error> {
      self.send_and_handle_frames()?;
      self.conn.send_content_frames(channel_id, 60, payload, properties);
      self.send_and_handle_frames()
  }

  fn send_frames(&mut self) -> Result<(), io::Error> {
    //FIXME: find a way to use a future here
    trace!("send frames");
    while let Some(f) = self.conn.next_frame() {
      if let Err(e) = self.send_frame(f) {
        error!("Failed to send frame: {:?}", e);
        return Err(e);
      }
    }
    Ok(())
  }

  fn send_frame(&mut self, frame: Frame) -> Poll<(), io::Error> {
      self.start_send(frame).and_then(|_| self.poll_complete())
  }

  fn handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    trace!("handle frames");
    for _ in 0..30 {
      if try_ready!(self.poll()).is_none() {
        return Ok(Async::Ready(None));
      }
    }
    self.poll()
  }
}

/// implements a future of `AMQPTransport`
///
/// this structure is used to perform the AMQP handshake and provide
/// a connected transport afterwards
pub struct AMQPTransportConnector<T> {
  pub transport: Option<AMQPTransport<T>>,
}

impl<T> Future for AMQPTransportConnector<T>
    where T: AsyncRead + AsyncWrite,
          T: Sync+Send,
          T: 'static {

  type Item  = AMQPTransport<T>;
  type Error = io::Error;

  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    debug!("AMQPTransportConnector poll transport is none? {}", self.transport.is_none());
    let mut transport = self.transport.take().unwrap();

    //we might have received a frame before here
    transport.send_and_handle_frames()?;

    debug!("conn state: {:?}", transport.conn.state);
    if transport.conn.state == ConnectionState::Connected {
      debug!("already connected");
      return Ok(Async::Ready(transport))
    }

    trace!("waiting before poll");
    match transport.poll()? {
      Async::Ready(Some(_)) => {
        if transport.conn.state == ConnectionState::Connected {
          // Upstream had frames available and we're connected, the transport is ready
          Ok(Async::Ready(transport))
        } else {
          // Upstream had frames but we're not yet connected, continue polling
          let poll_ret = transport.poll();
          self.transport = Some(transport);
          poll_ret?;
          Ok(Async::NotReady)
        }
      },
      _ => {
        // Upstream had no frames
        self.transport = Some(transport);
        Ok(Async::NotReady)
      },
    }
  }
}

impl<T> Stream for AMQPTransport<T>
    where T: AsyncRead + AsyncWrite,
          T: Sync+Send,
          T: 'static {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
      trace!("stream poll");
      self.poll_upstream()
    }
}

impl<T> Sink for AMQPTransport<T>
    where T: AsyncWrite,
          T: Sync+Send {
    type SinkItem = Frame;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Frame) -> StartSend<Frame, io::Error> {
        trace!("sink start send");
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        trace!("sink poll_complete");
        self.upstream.poll_complete()
    }
}
<commit_msg>futures: inline poll_upstream<commit_after>/// low level wrapper for the state machine, encoding and decoding from lapin-async
use lapin_async::connection::*;
use lapin_async::format::frame::*;

use nom::{IResult,Offset};
use cookie_factory::GenError;
use bytes::BytesMut;
use std::cmp;
use std::iter::repeat;
use std::io::{self,Error,ErrorKind};
use std::sync::{Arc,Mutex};
use std::thread;
use std::time::Duration;
use futures::{Async,Poll,Sink,Stream,StartSend,Future,future};
use tokio_core::reactor::Core;
use tokio_io::{AsyncRead,AsyncWrite};
use tokio_io::codec::{Decoder,Encoder,Framed};
use tokio_timer::Timer;
use channel::BasicProperties;
use client::ConnectionOptions;

/// implements tokio-io's Decoder and Encoder
pub struct AMQPCodec {
    pub frame_max: u32,
}

impl Decoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Frame>, io::Error> {
        let (consumed, f) = match frame(buf) {
          IResult::Incomplete(_) => {
            return Ok(None)
          },
          IResult::Error(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("parse error: {:?}", e)))
          },
          IResult::Done(i, frame) => {
            (buf.offset(i), frame)
          }
        };

        trace!("decoded frame: {:?}", f);

        buf.split_to(consumed);

        Ok(Some(f))
    }
}

impl Encoder for AMQPCodec {
    type Item = Frame;
    type Error = io::Error;

    fn encode(&mut self, frame: Frame, buf: &mut BytesMut) -> Result<(), Self::Error> {
      let length    = buf.len();
      // Ensure we at least allocate 8192 so that the buffer is big enough for the frame_max
      // negociation. Afterwards, use frame_max if > 8192.
      let frame_max = cmp::max(self.frame_max, 8192) as usize;
      if length < frame_max {
        //reserve more capacity and intialize it
        buf.extend(repeat(0).take(frame_max - length));
      }
      trace!("will encode and write frame: {:?}", frame);

      loop {
        let gen_res = match &frame {
          &Frame::ProtocolHeader => {
            gen_protocol_header((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Heartbeat(_) => {
            gen_heartbeat_frame((buf, 0)).map(|tup| tup.1)
          },
          &Frame::Method(channel, ref method) => {
            gen_method_frame((buf, 0), channel, method).map(|tup| tup.1)
          },
          &Frame::Header(channel_id, class_id, ref header) => {
            gen_content_header_frame((buf, 0), channel_id, class_id, header.body_size, &header.properties).map(|tup| tup.1)
          },
          &Frame::Body(channel_id, ref data) => {
            gen_content_body_frame((buf, 0), channel_id, data).map(|tup| tup.1)
          }
        };

        match gen_res {
          Ok(sz) => {
            buf.truncate(sz);
            trace!("serialized frame: {} bytes", sz);
            return Ok(());
          },
          Err(e) => {
            error!("error generating frame: {:?}", e);
            match e {
              GenError::BufferTooSmall(sz) => {
                buf.extend(repeat(0).take(sz - length));
                //return Err(Error::new(ErrorKind::InvalidData, "send buffer too small"));
              },
              GenError::InvalidOffset | GenError::CustomError(_) | GenError::NotYetImplemented => {
                return Err(Error::new(ErrorKind::InvalidData, "could not generate"));
              }
            }
          }
        }
      }
    }
}

/// Wrappers over a `Framed` stream using `AMQPCodec` and lapin-async's `Connection`
pub struct AMQPTransport<T> {
  upstream: Framed<T,AMQPCodec>,
  pub conn: Connection,
}

impl<T> AMQPTransport<T>
   where T: AsyncRead+AsyncWrite,
         T: Sync+Send,
         T: 'static               {

  /// starts the connection process
  ///
  /// returns a future of a `AMQPTransport` that is connected
  pub fn connect(stream: T, options: &ConnectionOptions) -> Box<Future<Item = AMQPTransport<T>, Error = io::Error>> {
    let mut conn = Connection::new();
    conn.set_credentials(&options.username, &options.password);
    conn.set_vhost(&options.vhost);
    conn.set_frame_max(options.frame_max);
    conn.set_heartbeat(options.heartbeat);
    if let Err(e) = conn.connect() {
      let err = format!("Failed to connect: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let codec = AMQPCodec {
      frame_max: conn.configuration.frame_max,
    };
    let mut t = AMQPTransport {
      upstream:  stream.framed(codec),
      conn:      conn,
    };

    if let Err(e) = t.send_and_handle_frames() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }

    let mut connector = AMQPTransportConnector {
      transport: Some(t),
    };

    trace!("pre-poll");
    if let Err(e) = connector.poll() {
      let err = format!("Failed to handle frames: {:?}", e);
      return Box::new(future::err(Error::new(ErrorKind::ConnectionAborted, err)));
    }
    trace!("post-poll");

    Box::new(connector)
  }

  pub fn start_heartbeat(&self, transport: Arc<Mutex<AMQPTransport<T>>>) -> io::Result<()> {
      let heartbeat = self.conn.configuration.heartbeat as u64;
      if heartbeat > 0 {
          thread::Builder::new().name("lapin heartbeat".to_string()).spawn(move || {
              Core::new().unwrap().run(Timer::default().interval(Duration::from_secs(heartbeat)).map_err(From::from).for_each(|_| {
                  debug!("poll heartbeat");
                  if let Ok(mut transport) = transport.lock() {
                      debug!("Sending heartbeat");
                      if let Err(e) = transport.send_frame(Frame::Heartbeat(0)) {
                          error!("Failed to send heartbeat: {:?}", e);
                          return Err(e);
                      } else {
                          Ok(())
                      }
                  } else {
                      Ok(())
                  }
              })).unwrap()
          })?;
      }
      Ok(())
  }

  pub fn send_and_handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    self.send_frames()?;
    self.handle_frames()
  }

  pub fn send_and_handle_frames_with_payload(&mut self, channel_id: u16, payload: &[u8], properties: BasicProperties) -> Poll<Option<()>, io::Error> {
      self.send_and_handle_frames()?;
      self.conn.send_content_frames(channel_id, 60, payload, properties);
      self.send_and_handle_frames()
  }

  fn send_frames(&mut self) -> Result<(), io::Error> {
    //FIXME: find a way to use a future here
    trace!("send frames");
    while let Some(f) = self.conn.next_frame() {
      if let Err(e) = self.send_frame(f) {
        error!("Failed to send frame: {:?}", e);
        return Err(e);
      }
    }
    Ok(())
  }

  fn send_frame(&mut self, frame: Frame) -> Poll<(), io::Error> {
      self.start_send(frame).and_then(|_| self.poll_complete())
  }

  fn handle_frames(&mut self) -> Poll<Option<()>, io::Error> {
    trace!("handle frames");
    for _ in 0..30 {
      if try_ready!(self.poll()).is_none() {
        return Ok(Async::Ready(None));
      }
    }
    self.poll()
  }
}

/// implements a future of `AMQPTransport`
///
/// this structure is used to perform the AMQP handshake and provide
/// a connected transport afterwards
pub struct AMQPTransportConnector<T> {
  pub transport: Option<AMQPTransport<T>>,
}

impl<T> Future for AMQPTransportConnector<T>
    where T: AsyncRead + AsyncWrite,
          T: Sync+Send,
          T: 'static {

  type Item  = AMQPTransport<T>;
  type Error = io::Error;

  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    debug!("AMQPTransportConnector poll transport is none? {}", self.transport.is_none());
    let mut transport = self.transport.take().unwrap();

    //we might have received a frame before here
    transport.send_and_handle_frames()?;

    debug!("conn state: {:?}", transport.conn.state);
    if transport.conn.state == ConnectionState::Connected {
      debug!("already connected");
      return Ok(Async::Ready(transport))
    }

    trace!("waiting before poll");
    match transport.poll()? {
      Async::Ready(Some(_)) => {
        if transport.conn.state == ConnectionState::Connected {
          // Upstream had frames available and we're connected, the transport is ready
          Ok(Async::Ready(transport))
        } else {
          // Upstream had frames but we're not yet connected, continue polling
          let poll_ret = transport.poll();
          self.transport = Some(transport);
          poll_ret?;
          Ok(Async::NotReady)
        }
      },
      _ => {
        // Upstream had no frames
        self.transport = Some(transport);
        Ok(Async::NotReady)
      },
    }
  }
}

impl<T> Stream for AMQPTransport<T>
    where T: AsyncRead + AsyncWrite,
          T: Sync+Send,
          T: 'static {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<()>, io::Error> {
        trace!("stream poll");
        let value = match self.upstream.poll() {
            Ok(Async::Ready(t)) => t,
            Ok(Async::NotReady) => {
                trace!("upstream poll gave NotReady");
                return Ok(Async::NotReady);
            },
            Err(e) => {
                error!("upstream poll gave error: {:?}", e);
                return Err(From::from(e));
            },
        };

        if let Some(frame) = value {
            trace!("upstream poll gave frame: {:?}", frame);
            if let Err(e) = self.conn.handle_frame(frame) {
                let err = format!("failed to handle frame: {:?}", e);
                return Err(io::Error::new(io::ErrorKind::Other, err));
            }
            self.send_frames()?;
            Ok(Async::Ready(Some(())))
        } else {
            error!("upstream poll gave Ready(None)");
            Ok(Async::Ready(None))
        }
    }
}

impl<T> Sink for AMQPTransport<T>
    where T: AsyncWrite,
          T: Sync+Send {
    type SinkItem = Frame;
    type SinkError = io::Error;

    fn start_send(&mut self, item: Frame) -> StartSend<Frame, io::Error> {
        trace!("sink start send");
        self.upstream.start_send(item)
    }

    fn poll_complete(&mut self) -> Poll<(), io::Error> {
        trace!("sink poll_complete");
        self.upstream.poll_complete()
    }
}
<|endoftext|>


<commit_before>// A generic struct
struct Pair<T> {
    first: T,
    second: T,
}

// A generic function
fn swap<T>(pair: Pair<T>) -> Pair<T> {
    let Pair { first: first, second: second } = pair;

    Pair { first: second, second: first }
}

// Reimplementation of a 2-element tuple
struct Tuple2<T, U>(T, U);

fn main() {
    // Explicitly specialize `Pair`
    let pair_of_chars: Pair<char> = Pair { first: 'a', second: 'b' };

    // Implicitly specialize `Pair`
    let pair_of_ints = Pair { first: 1, second: 2 };

    // Explicitly specialize `Tuple2`
    let _tuple: Tuple2<char, int> = Tuple2('R', 2);

    // Explicitly specialize `swap`
    let _swapped_pair_of_chars = swap::<char>(pair_of_chars);

    // Implicitly specialize `swap`
    let _swapped_pair_of_ints = swap(pair_of_ints);
}

<commit_msg>generics: Improve wording. Closes #92<commit_after>// A generic struct
struct Pair<T> {
    first: T,
    second: T,
}

// A generic function
fn swap<T>(pair: Pair<T>) -> Pair<T> {
    let Pair { first: first, second: second } = pair;

    Pair { first: second, second: first }
}

// Reimplementing a 2-element tuple as a tuple struct
struct Tuple2<T, U>(T, U);

fn main() {
    // Explicitly specialize `Pair`
    let pair_of_chars: Pair<char> = Pair { first: 'a', second: 'b' };

    // Implicitly specialize `Pair`
    let pair_of_ints = Pair { first: 1, second: 2 };

    // Explicitly specialize `Tuple2`
    let _tuple: Tuple2<char, int> = Tuple2('R', 2);

    // Explicitly specialize `swap`
    let _swapped_pair_of_chars = swap::<char>(pair_of_chars);

    // Implicitly specialize `swap`
    let _swapped_pair_of_ints = swap(pair_of_ints);
}

<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;

use futures::{future, stream, Future};
use lazy_static::lazy_static;

use spectral::prelude::*;
use speculate::speculate;

use gerritbot_spark as spark;
use spark::{EmailRef, PersonId, PersonIdRef};

use gerritbot::*;

#[derive(Debug, Clone, Default)]
struct TestGerritCommandRunner;
impl GerritCommandRunner for TestGerritCommandRunner {}

#[derive(Debug, Clone)]
struct Reply {
    person_id: PersonId,
    message: String,
}

type Replies = Rc<RefCell<Vec<Reply>>>;

#[derive(Debug, Clone, Default)]
struct TestSparkClient {
    replies: Replies,
}

impl SparkClient for TestSparkClient {
    type ReplyFuture = future::FutureResult<(), spark::Error>;
    fn reply(&self, person_id: &PersonId, msg: &str) -> Self::ReplyFuture {
        self.replies.borrow_mut().push(Reply {
            person_id: person_id.to_owned(),
            message: msg.to_string(),
        });
        future::ok(())
    }
}

type TestBot = Bot<TestGerritCommandRunner, TestSparkClient>;

lazy_static! {
    static ref TEST_PERSON_ID: &'static PersonIdRef = PersonIdRef::new("test_person_id");
    static ref TEST_PERSON_EMAIL: &'static EmailRef = EmailRef::new("test@person.test");
}

trait TestBotTrait: Sized {
    fn new() -> (Self, Replies);
    fn send_message(self, message: &str);
    fn send_messages(self, messages: &[&str]);
}

impl TestBotTrait for TestBot {
    fn new() -> (Self, Replies) {
        let replies = Replies::default();
        let bot = Builder::new(State::new()).build(
            Default::default(),
            TestSparkClient {
                replies: replies.clone(),
            },
        );
        (bot, replies)
    }

    fn send_messages(self, messages: &[&str]) {
        let spark_messages = stream::iter_ok(messages.iter().map(|msg| {
            spark::Message::test_message(
                TEST_PERSON_EMAIL.to_owned(),
                TEST_PERSON_ID.to_owned(),
                msg.to_string(),
            )
        }));
        let gerrit_events = stream::empty();
        self.run(gerrit_events, spark_messages).wait().unwrap();
    }

    fn send_message(self, message: &str) {
        self.send_messages(&[message][..]);
    }
}

speculate! {
    before {
        let (bot, replies) = TestBot::new();
    }

    describe "command tests" {
        test "status" {
            bot.send_message("status");
            let replies = Rc::try_unwrap(replies).unwrap().into_inner();
            assert_that!(replies).has_length(1);
            assert_that!(replies[0].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[0].message).contains("disabled");
        }

        test "enable then status" {
            bot.send_messages(&["enable", "status"][..]);
            let replies = Rc::try_unwrap(replies).unwrap().into_inner();
            assert_that!(replies).has_length(2);
            assert_that!(replies[0].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[0].message).contains("Happy reviewing!");
            assert_that!(replies[1].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[1].message).contains("enabled");
        }
    }
}
<commit_msg>unknown command test<commit_after>use std::cell::RefCell;
use std::rc::Rc;

use futures::{future, stream, Future};
use lazy_static::lazy_static;

use spectral::prelude::*;
use speculate::speculate;

use gerritbot_spark as spark;
use spark::{EmailRef, PersonId, PersonIdRef};

use gerritbot::*;

#[derive(Debug, Clone, Default)]
struct TestGerritCommandRunner;
impl GerritCommandRunner for TestGerritCommandRunner {}

#[derive(Debug, Clone)]
struct Reply {
    person_id: PersonId,
    message: String,
}

type Replies = Rc<RefCell<Vec<Reply>>>;

#[derive(Debug, Clone, Default)]
struct TestSparkClient {
    replies: Replies,
}

impl SparkClient for TestSparkClient {
    type ReplyFuture = future::FutureResult<(), spark::Error>;
    fn reply(&self, person_id: &PersonId, msg: &str) -> Self::ReplyFuture {
        self.replies.borrow_mut().push(Reply {
            person_id: person_id.to_owned(),
            message: msg.to_string(),
        });
        future::ok(())
    }
}

type TestBot = Bot<TestGerritCommandRunner, TestSparkClient>;

lazy_static! {
    static ref TEST_PERSON_ID: &'static PersonIdRef = PersonIdRef::new("test_person_id");
    static ref TEST_PERSON_EMAIL: &'static EmailRef = EmailRef::new("test@person.test");
}

trait TestBotTrait: Sized {
    fn new() -> (Self, Replies);
    fn send_message(self, message: &str);
    fn send_messages(self, messages: &[&str]);
}

impl TestBotTrait for TestBot {
    fn new() -> (Self, Replies) {
        let replies = Replies::default();
        let bot = Builder::new(State::new()).build(
            Default::default(),
            TestSparkClient {
                replies: replies.clone(),
            },
        );
        (bot, replies)
    }

    fn send_messages(self, messages: &[&str]) {
        let spark_messages = stream::iter_ok(messages.iter().map(|msg| {
            spark::Message::test_message(
                TEST_PERSON_EMAIL.to_owned(),
                TEST_PERSON_ID.to_owned(),
                msg.to_string(),
            )
        }));
        let gerrit_events = stream::empty();
        self.run(gerrit_events, spark_messages).wait().unwrap();
    }

    fn send_message(self, message: &str) {
        self.send_messages(&[message][..]);
    }
}

speculate! {
    before {
        let (bot, replies) = TestBot::new();
    }

    describe "command tests" {
        test "status" {
            bot.send_message("status");
            let replies = Rc::try_unwrap(replies).unwrap().into_inner();
            assert_that!(replies).has_length(1);
            assert_that!(replies[0].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[0].message).contains("disabled");
        }

        test "enable then status" {
            bot.send_messages(&["enable", "status"][..]);
            let replies = Rc::try_unwrap(replies).unwrap().into_inner();
            assert_that!(replies).has_length(2);
            assert_that!(replies[0].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[0].message).contains("Happy reviewing!");
            assert_that!(replies[1].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[1].message).contains("enabled");
        }

        test "unknown command" {
            bot.send_message("this is not a known command");
            let replies = Rc::try_unwrap(replies).unwrap().into_inner();
            assert_that!(replies).has_length(1);
            assert_that!(replies[0].person_id).is_equal_to(TEST_PERSON_ID.to_owned());
            assert_that!(replies[0].message).contains("I am GerritBot");
        }
    }
}
<|endoftext|>


<commit_before>use redox::*;

mod cmd;

#[derive(Copy, Clone)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
         if self.offset > 0 {
             window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
             self.string = self.string[0 .. self.offset - 1].to_string() +
                 &self.string[self.offset .. self.string.len()];
             self.offset -= 1;
         }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset + 1].to_string() +
                &self.string[self.offset + 1 .. self.string.len() - 1];
        }
    }

    fn up(&mut self) {
        let mut new_offset = 0;
        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let mut new_offset = self.string.len();
        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
                                     (rand() % 300 + 50) as isize,
                                     576,
                                     400,
                                      &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();
        let mut multiplier: Option<u32> = None;

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        cmd::exec(self, &mut mode, &mut multiplier, &mut last_change, key_event, &mut window);

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<commit_msg>Fix delete/x command<commit_after>use redox::*;

mod cmd;

#[derive(Copy, Clone)]
pub enum Mode {
    Insert,
    Normal,
}

pub struct Editor {
    url: String,
    file: Option<File>,
    string: String,
    offset: usize,
    scroll_x: isize,
    scroll_y: isize,
}

impl Editor {
    #[inline(never)]
    pub fn new() -> Self {
        Editor {
            url: String::new(),
            file: Option::None,
            string: String::new(),
            offset: 0,
            scroll_x: 0,
            scroll_y: 0,
        }
    }

    fn backspace(&mut self, window: &mut Window) {
         if self.offset > 0 {
             window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
             self.string = self.string[0 .. self.offset - 1].to_string() +
                 &self.string[self.offset .. self.string.len()];
             self.offset -= 1;
         }
    }

    fn delete(&mut self, window: &mut Window) {
        if self.offset < self.string.len() {
            window.set_title(&format!("{}{}{}","Editor (", &self.url, ") Changed"));
            self.string = self.string[0 .. self.offset].to_string() +
                &self.string[self.offset + 1 .. self.string.len()];
        }
    }

    fn up(&mut self) {
        let mut new_offset = 0;
        for i in 2..self.offset {
            match self.string.as_bytes()[self.offset - i] {
                0 => break,
                10 => {
                    new_offset = self.offset - i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn left(&mut self) {
        if self.offset > 0 {
            self.offset -= 1;
        }
    }

    fn right(&mut self) {
        if self.offset < self.string.len() {
            self.offset += 1;
        }
    }

    fn down(&mut self) {
        let mut new_offset = self.string.len();
        for i in self.offset..self.string.len() {
            match self.string.as_bytes()[i] {
                0 => break,
                10 => {
                    new_offset = i + 1;
                    break;
                }
                _ => (),
            }
        }
        self.offset = new_offset;
    }

    fn reload(&mut self, window: &mut Window) {
        window.set_title(&("Editor (".to_string() + &self.url + ")"));
        self.offset = 0;
        self.scroll_x = 0;
        self.scroll_y = 0;

        match self.file {
            Option::Some(ref mut file) => {
                file.seek(Seek::Start(0));
                let mut vec: Vec<u8> = Vec::new();
                file.read_to_end(&mut vec);
                self.string = unsafe { String::from_utf8_unchecked(vec) };
            },
            Option::None => self.string = String::new(),
        }
    }

    fn save(&mut self, window: &mut Window) {
        match self.file {
            Option::Some(ref mut file) => {
                window.set_title(&("Editor (".to_string() + &self.url + ") Saved"));
                file.seek(Seek::Start(0));
                file.write(&self.string.as_bytes());
                file.sync();
            }
            Option::None => {
                //TODO: Ask for file to save to
                window.set_title(&("Editor (".to_string() + &self.url + ") No Open File"));
            }
        }
    }

    fn draw_content(&mut self, window: &mut Window) {
        let mut redraw = false;

        {
            window.set([255, 255, 255, 255]);

            let scroll_x = self.scroll_x;
            let scroll_y = self.scroll_y;

            let mut offset = 0;

            let mut col = -scroll_x;
            let cols = window.width() as isize / 8;

            let mut row = -scroll_y;
            let rows = window.height() as isize / 16;

            for c in self.string.chars() {
                if offset == self.offset {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                    } else {
                        if col < 0 { //Too far to the left
                            self.scroll_x += col;
                        } else if col >= cols { //Too far to the right
                            self.scroll_x += cols - col + 1;
                        }
                        if row < 0 { //Too far up
                            self.scroll_y += row;
                        } else if row >= rows { //Too far down
                            self.scroll_y += rows - row + 1;
                        }

                        redraw = true;
                    }
                }

                if c == '\n' {
                    col = -scroll_x;
                    row += 1;
                } else if c == '\t' {
                    col += 8 - col % 8;
                } else {
                    if col >= 0 && col < cols && row >= 0 && row < rows {
                        window.char(8 * col, 16 * row, c, [0, 0, 0, 255]);
                    }
                    col += 1;
                }

                offset += 1;
            }

            if offset == self.offset {
                if col >= 0 && col < cols && row >= 0 && row < rows {
                    window.char(8 * col, 16 * row, '_', [128, 128, 128, 255]);
                } else {
                    if col < 0 { //Too far to the left
                        self.scroll_x += col;
                    } else if col >= cols { //Too far to the right
                        self.scroll_x += cols - col + 1;
                    }
                    if row < 0 { //Too far up
                        self.scroll_y += row;
                    } else if row >= rows { //Too far down
                        self.scroll_y += rows - row + 1;
                    }

                    redraw = true;
                }
            }

            window.sync();
        }

        if redraw {
            self.draw_content(window);
        }
    }

    fn main(&mut self, url: &str) {
        let mut window = Window::new((rand() % 400 + 50) as isize,
                                     (rand() % 300 + 50) as isize,
                                     576,
                                     400,
                                      &("Editor (".to_string() + url + ")"));

        self.url = url.to_string();
        self.file = Option::Some(File::open(&self.url));

        self.reload(&mut window);
        self.draw_content(&mut window);

        let mut mode = Mode::Normal;

        let mut last_change = String::new();
        let mut multiplier: Option<u32> = None;

        while let Option::Some(event) = window.poll() {
            match event.to_option() {
                EventOption::Key(key_event) => {
                    if key_event.pressed {
                        cmd::exec(self, &mut mode, &mut multiplier, &mut last_change, key_event, &mut window);

                        self.draw_content(&mut window);
                    }
                }
                _ => (),
            }
        }
    }
}

pub fn main() {
    match args().get(1) {
        Option::Some(arg) => Editor::new().main(&arg),
        Option::None => Editor::new().main("none://"),
    }
}
<|endoftext|>


<commit_before>use std::convert::From;

use binding::class::{define_class, new_instance, define_method, define_singleton_method};
use binding::global::rb_cObject;
use binding::util::get_constant;
use types::{Callback, Value};
use util::create_arguments;

use super::any_object::AnyObject;
use super::traits::Object;

pub struct Class {
    value: Value
}

impl Class {
    // TODO: replace rb_cObject with optional superclass
    pub fn new(name: &str) -> Self {
        Class {
            value: define_class(name, rb_cObject)
        }
    }

    // TODO: replace rb_cObject with optional class/module value
    pub fn from_existing(name: &str) -> Self {
        Class {
            value: get_constant(name, rb_cObject)
        }
    }

    pub fn new_instance(&self, arguments: Vec<AnyObject>) -> AnyObject {
        let (argc, argv) = create_arguments(arguments);
        let instance = new_instance(self.value(), argc, argv);

        AnyObject::from(instance)
    }

    pub fn define_method<T: Object>(&self, name: &str, callback: Callback<T>) {
        define_method::<T>(self.value, name, callback);
    }

    pub fn define_singleton_method<T: Object>(&self, name: &str, callback: Callback<T>) {
        define_singleton_method(self.value, name, callback);
    }
}

impl From<Value> for Class {
    fn from(value: Value) -> Self {
        Class {
            value: value
        }
    }
}

impl Object for Class {
    fn value(&self) -> Value {
        self.value
    }
}
<commit_msg>Create Class::def() and Class::def_self()<commit_after>use std::convert::From;

use binding::class::{define_class, new_instance, define_method, define_singleton_method};
use binding::global::rb_cObject;
use binding::util::get_constant;
use types::{Callback, Value};
use util::create_arguments;

use super::any_object::AnyObject;
use super::traits::Object;

pub struct Class {
    value: Value
}

impl Class {
    // TODO: replace rb_cObject with optional superclass
    pub fn new(name: &str) -> Self {
        Class {
            value: define_class(name, rb_cObject)
        }
    }

    // TODO: replace rb_cObject with optional class/module value
    pub fn from_existing(name: &str) -> Self {
        Class {
            value: get_constant(name, rb_cObject)
        }
    }

    pub fn new_instance(&self, arguments: Vec<AnyObject>) -> AnyObject {
        let (argc, argv) = create_arguments(arguments);
        let instance = new_instance(self.value(), argc, argv);

        AnyObject::from(instance)
    }

    pub fn define_method<T: Object>(&self, name: &str, callback: Callback<T>) {
        define_method::<T>(self.value, name, callback);
    }

    pub fn define_singleton_method<T: Object>(&self, name: &str, callback: Callback<T>) {
        define_singleton_method(self.value, name, callback);
    }

    pub fn def<T: Object>(&self, name: &str, callback: Callback<T>) {
        self.define_method(name, callback);
    }

    pub fn def_self<T: Object>(&self, name: &str, callback: Callback<T>) {
        self.define_singleton_method(name, callback);
    }
}

impl From<Value> for Class {
    fn from(value: Value) -> Self {
        Class {
            value: value
        }
    }
}

impl Object for Class {
    fn value(&self) -> Value {
        self.value
    }
}
<|endoftext|>


<commit_before>
use buffer::{ImageBuffer, Pixel, ArrayLike};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

use std::old_io;
use std::old_io::{MemReader, IoResult};
use std::num::Int;

use color::{Rgb, Rgba};
use utils::lzw;
use utils::bitstream::LsbWriter;

use super::{Extension, Block, DisposalMethod};

#[derive(Debug, Copy)]
#[allow(unused_qualifications)]
/// The color mode the encoder will use to encode the image.
pub enum ColorMode {
	/// Image will be encoded in multiple frames if more than 256 colors are present
	TrueColor,
	/// Number of colors will be reduced
	Indexed(u8),
}
use self::ColorMode::{TrueColor, Indexed};

/// A GIF encoder.
///
/// Encodes the image either in true color using indexed colors.
/// If the mode is set to TrueColor the image is split into multiple frames
/// when the number of colors including transparent color exceeds 256.
/// Pixels with an alpha value != 1.0 will be set to alpha = 0.
pub struct Encoder<Image> {
	image: Image,
	bg_color: Option<Rgb<u8>>,
	color_mode: ColorMode
}

const TRANSPARENT: Rgba<u8> = Rgba([0, 0, 0, 0]);

impl<Container> Encoder<ImageBuffer<Rgba<u8>, Container>>
where Container: ArrayLike<u8> {
	/// Creates a new GIF encoder
	pub fn new(image: ImageBuffer<Rgba<u8>, Container>,
		       bg_color: Option<Rgb<u8>>,
		       color_mode: ColorMode,
		      ) -> Encoder<ImageBuffer<Rgba<u8>, Container>> {
		Encoder {
			image: image,
			bg_color: bg_color,
			color_mode: color_mode
		}
	}

	/// Encodes the image
	pub fn encode<W: Writer>(&mut self, w: &mut W) -> IoResult<()> {
		// Header
		try!(w.write_all(b"GIF89a"));
		// Logical screen descriptor
		let height = self.image.height();
		let width = self.image.width();
		if width > <u16 as Int>::max_value() as u32 ||
		   height > <u16 as Int>::max_value() as u32 {
		   	return Err(old_io::IoError{
		   		kind: old_io::InvalidInput,
		   		desc: "Image dimensions are to large for the gif format.",
		   		detail: None
		   	})
		}
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));

		let (hist, transparent) = self.histogram();
		let num_colors = hist.len();

		try!(self.write_global_table(w, &hist[]));

		match self.color_mode {
			// Global color table has been written, just write the image data now
			TrueColor if num_colors <= 256 => {
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_data_simple(w, &hist[], transparent));
			},
			TrueColor => {
				panic!("TrueColor unimplemented")
			},
			Indexed(n) if n as usize <= num_colors => {
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_data_simple(w, &hist[], transparent));
			}
			Indexed(_) => {
				panic!("Indexed(n) unimplemented")
			}

		}
		w.write_u8(Block::Trailer as u8)
	}

	/// Returns a histogram of the colors in the image
	fn histogram(&self) -> (Vec<(Rgba<u8>, usize)>, Option<usize>){
		let mut hist: HashMap<_, usize> = HashMap::new();
		// Add bg_color to table
		if let Some(bg_color) = self.bg_color {
			let _ = hist.insert(bg_color.to_rgba(), 0);
		}
		for p in self.image.pixels() {
			let mut p = *p;
			// Replace transparent pixels with black, alpha = 0
			if let Some(bg_color) = self.bg_color {
				p.blend(&bg_color.to_rgba())
			} else if p[3] != 255 {
				p = TRANSPARENT;
			}
			match hist.entry(p) {
				Occupied(mut entry) => {
					let val = entry.get_mut();
					*val = *val + 1;
				},
				Vacant(entry) => {
					entry.insert(1);
				}
			}
		}
		let mut colors: Vec<(Rgba<u8>, usize)> = hist.into_iter().collect();
		// Sort color map with decreasing color freqs
		colors.sort_by(|a, b| b.1.cmp(&a.1));
		// Add bg_color to table
		let transparent = colors.iter().position(|x| x.0 == TRANSPARENT);
		(colors, transparent)
	}

	/// Write the global color table and the corresponding flags
	fn write_global_table<W: Writer>(&mut self,
									 w: &mut W,
									 hist: &[(Rgba<u8>, usize)]
									) -> IoResult<()>
	{
		let num_colors = hist.len();
		let mut flags = 0;
		flags |= 1 << 4; // sort flag
		let n = flag_n(num_colors);
		flags |= n << 4; // 2^(n+1) colors
		let (global_table, bg_index) = if num_colors <= 256 {
			flags |= 1 << 7; // glocal table flag
			flags |= n;
			let mut bg_index = 0;
			let mut table = Vec::with_capacity(3*(2 << n));
			for (i, &(color, _)) in hist.iter().enumerate() {
				let channels = &color.channels()[..3];
				if let Some(bg_color) = self.bg_color {
					if channels == bg_color.channels() {
						bg_index = i;
					}
				}
				table.push_all(channels)
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_colors) {
				table.push_all(&[0, 0, 0]);
			}
			(Some(table), bg_index as u8)
		} else if let Some(bg_color) = self.bg_color {
			flags |= 1 << 7; // glocal table flag
			let mut table = Vec::with_capacity(6);
			table.push_all(&bg_color.channels()[..2]);
			table.push_all(&[0, 0, 0]);
			(Some(table), 0)
		} else {
			(None, 0)
		};
		try!(w.write_u8(flags));
		try!(w.write_u8(bg_index));
		try!(w.write_u8(0)); // aspect ration, disregard
		if let Some(global_table) = global_table {
			try!(w.write_all(&global_table[]));
		}
		Ok(())
	}

	/// Writes the graphics control extension
	fn write_control_ext<W: Writer>(&mut self,
								    w: &mut W,
								    delay: u16,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Extension as u8));
		try!(w.write_u8(Extension::Control as u8));
		try!(w.write_u8(4)); // size
		let mut field = 0;
		field |= (DisposalMethod::None as u8) << 2;
		let idx = if let Some(idx) = transparent {
			field |=  1;
			idx as u8
		} else {
			0
		};
		try!(w.write_u8(field));
		try!(w.write_le_u16(delay));
		try!(w.write_u8(idx));
		w.write_u8(0)
	}

	/// Writes the image descriptor
	fn write_descriptor<W: Writer>(&mut self,
								    w: &mut W,
								    table_len: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Image as u8));
		try!(w.write_le_u16(0)); // left
		try!(w.write_le_u16(0)); // top
		let height = self.image.height();
		let width = self.image.width();
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));
		if let Some(len) = table_len {
			w.write_u8(flag_n(len) | 0x80)
		} else {
			w.write_u8(0)
		}
	}

	/// Writes the image to the file assuming that every pixel is in the color table
	/// If not, the index of the transparent pixel is written
	fn write_data_simple<W: Writer>(&mut self,
								    w: &mut W,
								    hist: &[(Rgba<u8>, usize)],
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		let code_size = match flag_n(hist.len()) + 1 {
			1 => 2,
			n => n
		};
		let t_idx = match transparent { Some(i) => i as u8, None => 0 };
		let data: Vec<u8> = self.image.pixels().map(|p| {
			if let Some(idx) = hist.iter().position(|x| x.0 == *p) {
				idx as u8
			} else {
				t_idx
			}
		}).collect();
		let mut encoded_data = Vec::new();
		try!(lzw::encode(MemReader::new(data), LsbWriter::new(&mut encoded_data), code_size));
		try!(w.write_u8(code_size));
		for chunk in encoded_data.chunks(255) {
			try!(w.write_u8((chunk.len()) as u8));
			try!(w.write_all(chunk));
		}
		w.write_u8(0) // block terminator

	}
}

// Color table len converted to flag bits
fn flag_n(size: usize) -> u8 {
	match size {
		0  ...2   => 0,
		3  ...4   => 1,
		5  ...8   => 2,
		7  ...16  => 3,
		17 ...32  => 4,
		33 ...64  => 5,
		65 ...128 => 6,
		129...256 => 7,
		_ => 7
	}
}<commit_msg>Working but slow version<commit_after>
use buffer::{ImageBuffer, Pixel, ArrayLike};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

use std::old_io;
use std::old_io::{MemReader, IoResult};
use std::num::Int;

use color::{Rgb, Rgba};
use utils::lzw;
use utils::bitstream::LsbWriter;

use super::{Extension, Block, DisposalMethod};

#[derive(Debug, Copy)]
#[allow(unused_qualifications)]
/// The color mode the encoder will use to encode the image.
pub enum ColorMode {
	/// Image will be encoded in multiple frames if more than 256 colors are present
	TrueColor,
	/// Number of colors will be reduced
	Indexed(u8),
}
use self::ColorMode::{TrueColor, Indexed};

/// A GIF encoder.
///
/// Encodes the image either in true color using indexed colors.
/// If the mode is set to TrueColor the image is split into multiple frames
/// when the number of colors including transparent color exceeds 256.
/// Pixels with an alpha value != 1.0 will be set to alpha = 0.
pub struct Encoder<Image> {
	image: Image,
	bg_color: Option<Rgb<u8>>,
	color_mode: ColorMode
}

const TRANSPARENT: Rgba<u8> = Rgba([0, 0, 0, 0]);

impl<Container> Encoder<ImageBuffer<Rgba<u8>, Container>>
where Container: ArrayLike<u8> {
	/// Creates a new GIF encoder
	pub fn new(image: ImageBuffer<Rgba<u8>, Container>,
		       bg_color: Option<Rgb<u8>>,
		       color_mode: ColorMode,
		      ) -> Encoder<ImageBuffer<Rgba<u8>, Container>> {
		Encoder {
			image: image,
			bg_color: bg_color,
			color_mode: color_mode
		}
	}

	/// Encodes the image
	pub fn encode<W: Writer>(&mut self, w: &mut W) -> IoResult<()> {
		// Header
		try!(w.write_all(b"GIF89a"));
		// Logical screen descriptor
		let height = self.image.height();
		let width = self.image.width();
		if width > <u16 as Int>::max_value() as u32 ||
		   height > <u16 as Int>::max_value() as u32 {
		   	return Err(old_io::IoError{
		   		kind: old_io::InvalidInput,
		   		desc: "Image dimensions are to large for the gif format.",
		   		detail: None
		   	})
		}
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));

		let (hist, transparent) = self.histogram();
		let num_colors = hist.len();

		try!(self.write_global_table(w, &hist[]));

		match self.color_mode {
			// Global color table has been written, just write the image data now
			TrueColor if num_colors <= 256 => {
				// NOTE: copy paste any changes to case: Indexed(n) if n as usize <= num_colors
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], 0, transparent));
			},
			TrueColor => {
				try!(self.write_true_color(w, hist, transparent));
			},
			Indexed(n) if n as usize <= num_colors => {
				// NOTE: copy paste from case: TrueColor if num_colors <= 256
				try!(self.write_control_ext(w, 0, transparent));
				try!(self.write_descriptor(w, None));
				try!(self.write_image_simple(w, &hist[], 0, transparent));
			}
			Indexed(_) => {
				panic!("Indexed(n) unimplemented")
			}

		}
		w.write_u8(Block::Trailer as u8)
	}

	/// Returns a histogram of the colors in the image
	fn histogram(&self) -> (Vec<(Rgba<u8>, usize)>, Option<usize>){
		let mut hist: HashMap<_, usize> = HashMap::new();
		// Add bg_color to table
		if let Some(bg_color) = self.bg_color {
			let _ = hist.insert(bg_color.to_rgba(), 0);
		}
		for p in self.image.pixels() {
			let mut p = *p;
			// Replace transparent pixels with black, alpha = 0
			if let Some(bg_color) = self.bg_color {
				p.blend(&bg_color.to_rgba())
			} else if p[3] != 255 {
				p = TRANSPARENT;
			}
			match hist.entry(p) {
				Occupied(mut entry) => {
					let val = entry.get_mut();
					*val = *val + 1;
				},
				Vacant(entry) => {
					entry.insert(1);
				}
			}
		}
		let mut colors: Vec<(Rgba<u8>, usize)> = hist.into_iter().collect();
		// Sort color map with decreasing color freqs
		colors.sort_by(|a, b| b.1.cmp(&a.1));
		// Add bg_color to table
		let transparent = colors.iter().position(|x| x.0 == TRANSPARENT);
		(colors, transparent)
	}

	/// Write the global color table and the corresponding flags
	fn write_global_table<W: Writer>(&mut self,
									 w: &mut W,
									 hist: &[(Rgba<u8>, usize)]
									) -> IoResult<()>
	{
		let num_colors = hist.len();
		let mut flags = 0;
		flags |= 1 << 4; // sort flag
		let n = flag_n(num_colors);
		flags |= n << 4; // 2^(n+1) colors
		let (global_table, bg_index) = if num_colors <= 256 {
			flags |= 1 << 7; // glocal table flag
			flags |= n;
			let mut bg_index = 0;
			let mut table = Vec::with_capacity(3*(2 << n));
			for (i, &(color, _)) in hist.iter().enumerate() {
				let channels = &color.channels()[..3];
				if let Some(bg_color) = self.bg_color {
					if channels == bg_color.channels() {
						bg_index = i;
					}
				}
				table.push_all(channels)
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_colors) {
				table.push_all(&[0, 0, 0]);
			}
			(Some(table), bg_index as u8)
		} else if let Some(bg_color) = self.bg_color {
			flags |= 1 << 7; // glocal table flag
			let mut table = Vec::with_capacity(6);
			table.push_all(&bg_color.channels()[..3]);
			table.push_all(&[0, 0, 0]);
			(Some(table), 0)
		} else {
			(None, 0)
		};
		try!(w.write_u8(flags));
		try!(w.write_u8(bg_index));
		try!(w.write_u8(0)); // aspect ration, disregard
		if let Some(global_table) = global_table {
			try!(w.write_all(&global_table[]));
		}
		Ok(())
	}

	/// Writes the graphics control extension
	fn write_control_ext<W: Writer>(&mut self,
								    w: &mut W,
								    delay: u16,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Extension as u8));
		try!(w.write_u8(Extension::Control as u8));
		try!(w.write_u8(4)); // size
		let mut field = 0;
		field |= (DisposalMethod::None as u8) << 2;
		let idx = if let Some(idx) = transparent {
			field |=  1;
			idx as u8
		} else {
			0
		};
		try!(w.write_u8(field));
		try!(w.write_le_u16(delay));
		try!(w.write_u8(idx));
		w.write_u8(0)
	}

	/// Writes the image descriptor
	fn write_descriptor<W: Writer>(&mut self,
								    w: &mut W,
								    table_len: Option<usize>
								   ) -> IoResult<()>
	{
		try!(w.write_u8(Block::Image as u8));
		try!(w.write_le_u16(0)); // left
		try!(w.write_le_u16(0)); // top
		let height = self.image.height();
		let width = self.image.width();
		try!(w.write_le_u16(width as u16));
		try!(w.write_le_u16(height as u16));
		if let Some(len) = table_len {
			w.write_u8(flag_n(len) | 0x80)
		} else {
			w.write_u8(0)
		}
	}

	/// Writes and compresses the indexed data
	fn write_indices<W: Writer>(&mut self,
								    w: &mut W,
								    indices: &[u8],
								   ) -> IoResult<()>
	{
		let code_size = match flag_n(indices.len()) + 1 {
			1 => 2,
			n => n
		};
		let mut encoded_data = Vec::new();
		try!(lzw::encode(indices, LsbWriter::new(&mut encoded_data), code_size));
		try!(w.write_u8(code_size));
		for chunk in encoded_data.chunks(255) {
			try!(w.write_u8((chunk.len()) as u8));
			try!(w.write_all(chunk));
		}
		w.write_u8(0) // block terminator

	}

	/// Writes the image to the file assuming that every pixel is in the color table
	/// If not, the index of the transparent pixel is written
	fn write_image_simple<W: Writer>(&mut self,
								    w: &mut W,
								    hist: &[(Rgba<u8>, usize)],
								    offset: u8,
								    transparent: Option<usize>,
								   ) -> IoResult<()>
	{
		let t_idx = match transparent { Some(i) => i as u8, None => 0 };
		let data: Vec<u8> = self.image.pixels().map(|p| {
			if let Some(idx) = hist.iter().position(|x| x.0 == *p) {
				idx as u8 + offset
			} else {
				t_idx
			}
		}).collect();
		self.write_indices(w, &data[])

	}

	/// Writes the graphics control extension
	fn write_true_color<W: Writer>(&mut self,
								    w: &mut W,
								    hist: Vec<(Rgba<u8>, usize)>,
								    transparent: Option<usize>
								   ) -> IoResult<()>
	{
		let mut hist = hist;
		// We need a transparent pixel we put it to index 0
		if let Some(transparent) = transparent {
			let val = hist.swap_remove(transparent);
			hist.push(val);
		} else {
			hist.reserve(1);
			hist.push((TRANSPARENT, 0));
		}
		let val = hist.swap_remove(0);
		hist.push(val);
		let transparent = Some(0);
		for chunk in hist[1..].chunks(255) {
			// we inject the transparent color in every table thus => +1
			let num_local_colors = chunk.len() + 1;
			let n = flag_n(num_local_colors);
			try!(self.write_control_ext(w, 0, transparent));
			try!(self.write_descriptor(w, Some(num_local_colors)));
			// Transparent color
			try!(w.write_all(&TRANSPARENT.channels()[..3]));
			for &(color, _) in chunk.iter() {
				try!(w.write_all(&color.channels()[..3]));
			}
			// Waste some space as of gif spec
			for _ in 0..((2 << n) - num_local_colors) {
				try!(w.write_all(&[0, 0, 0]));
			}
			try!(self.write_image_simple(w, chunk, 1, transparent));
		}
		Ok(())
	}
}

// Color table len converted to flag bits
fn flag_n(size: usize) -> u8 {
	match size {
		0  ...2   => 0,
		3  ...4   => 1,
		5  ...8   => 2,
		7  ...16  => 3,
		17 ...32  => 4,
		33 ...64  => 5,
		65 ...128 => 6,
		129...256 => 7,
		_ => 7
	}
}<|endoftext|>


<commit_before>use std::process::Command;
use std::str;

static PROGNAME: &'static str = "./env";

#[test]
fn test_single_name_value_pair() {
    let po = Command::new(PROGNAME)
        .arg("FOO=bar")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert!(out.lines_any().any(|line| line == "FOO=bar"));
}

#[test]
fn test_multiple_name_value_pairs() {
    let po = Command::new(PROGNAME)
        .arg("FOO=bar")
        .arg("ABC=xyz")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out.lines_any().filter(|&line| line == "FOO=bar" || line == "ABC=xyz").count(), 2);
}

#[test]
fn test_ignore_environment() {
    let po = Command::new(PROGNAME)
        .arg("-i")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "");

    let po = Command::new(PROGNAME)
        .arg("-")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "");
}

#[test]
fn test_null_delimiter() {
    let po = Command::new(PROGNAME)
        .arg("-i")
        .arg("--null")
        .arg("FOO=bar")
        .arg("ABC=xyz")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "FOO=bar\0ABC=xyz\0");
}

#[test]
fn test_unset_variable() {
    // This test depends on the HOME variable being pre-defined by the
    // default shell
    let po = Command::new(PROGNAME)
        .arg("-u")
        .arg("HOME")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out.lines_any().any(|line| line.starts_with("HOME=")), false);
}
<commit_msg>Replace deprecated lines_any() with lines().<commit_after>use std::process::Command;
use std::str;

static PROGNAME: &'static str = "./env";

#[test]
fn test_single_name_value_pair() {
    let po = Command::new(PROGNAME)
        .arg("FOO=bar")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert!(out.lines().any(|line| line == "FOO=bar"));
}

#[test]
fn test_multiple_name_value_pairs() {
    let po = Command::new(PROGNAME)
        .arg("FOO=bar")
        .arg("ABC=xyz")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out.lines().filter(|&line| line == "FOO=bar" || line == "ABC=xyz").count(), 2);
}

#[test]
fn test_ignore_environment() {
    let po = Command::new(PROGNAME)
        .arg("-i")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "");

    let po = Command::new(PROGNAME)
        .arg("-")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "");
}

#[test]
fn test_null_delimiter() {
    let po = Command::new(PROGNAME)
        .arg("-i")
        .arg("--null")
        .arg("FOO=bar")
        .arg("ABC=xyz")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out, "FOO=bar\0ABC=xyz\0");
}

#[test]
fn test_unset_variable() {
    // This test depends on the HOME variable being pre-defined by the
    // default shell
    let po = Command::new(PROGNAME)
        .arg("-u")
        .arg("HOME")
        .output()
        .unwrap_or_else(|err| panic!("{}", err));

    let out = str::from_utf8(&po.stdout[..]).unwrap();
    assert_eq!(out.lines().any(|line| line.starts_with("HOME=")), false);
}
<|endoftext|>


<commit_before>use std::os::windows::fs::OpenOptionsExt;
use libc::DWORD;
use ::util::tmpname;

const ACCESS: DWORD     = libc::FILE_GENERIC_READ
                        | libc::FILE_GENERIC_WRITE;
const SHARE_MODE: DWORD = libc::FILE_SHARE_DELETE
                        | libc::FILE_SHARE_READ
                        | libc::FILE_SHARE_WRITE;
const FLAGS: DWORD      = libc::FILE_ATTRIBUTE_HIDDEN
                        | libc::FILE_ATTRIBUTE_TEMPORARY
                        | libc::FILE_FLAG_DELETE_ON_CLOSE;

pub fn create(dir: &Path) -> io::Result<File> {
    let opts = OpenOptions::new()
        .desired_access(ACCESS)
        .share_mode(SHARE)
        .creation_disposition(libc::CREATE_NEW)
        .flags_and_attributes(ATTRS);

    loop {
        return match opts.open(dir.join(tmpname())) {
            Ok(f) => Ok(f),
            Err(e) if e.kind() == io::Error::AlreadyExists => continue,
            Err(e) => Err(e),
        };
    }
}

pub fn reopen(f: &File) -> io::Result<File> {
    let h = f.as_raw_handle();
    match ReOpenFile(h, ACCESS, SHARE_MODE, FLAGS) {
        libc::INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
        h => Ok(FromRawHandle::from_raw_handle(h))
    }
}
<commit_msg>Fix windows imports<commit_after>use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::io::{FromRawHandle, AsRawHandle};
use std::path::Path;
use std::io;
use std::fs::{File, OpenOptions};
use ::libc::{self, DWORD};
use ::util::tmpname;

const ACCESS: DWORD     = libc::FILE_GENERIC_READ
                        | libc::FILE_GENERIC_WRITE;
const SHARE_MODE: DWORD = libc::FILE_SHARE_DELETE
                        | libc::FILE_SHARE_READ
                        | libc::FILE_SHARE_WRITE;
const FLAGS: DWORD      = libc::FILE_ATTRIBUTE_HIDDEN
                        | libc::FILE_ATTRIBUTE_TEMPORARY
                        | libc::FILE_FLAG_DELETE_ON_CLOSE;

pub fn create(dir: &Path) -> io::Result<File> {
    let opts = OpenOptions::new()
        .desired_access(ACCESS)
        .share_mode(SHARE)
        .creation_disposition(libc::CREATE_NEW)
        .flags_and_attributes(ATTRS);

    loop {
        return match opts.open(dir.join(tmpname())) {
            Ok(f) => Ok(f),
            Err(e) if e.kind() == io::Error::AlreadyExists => continue,
            Err(e) => Err(e),
        };
    }
}

pub fn reopen(f: &File) -> io::Result<File> {
    let h = f.as_raw_handle();
    match ReOpenFile(h, ACCESS, SHARE_MODE, FLAGS) {
        libc::INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
        h => Ok(FromRawHandle::from_raw_handle(h))
    }
}
<|endoftext|>


<commit_before>use std::io::process::Command;

static PANGRAM: &'static str =
"the quick brown fox jumped over the lazy dog\n";

fn main() {
    // Spawn the `wc` command
    let mut process = match Command::new("wc").spawn() {
        Err(why) => panic!("couldn't spawn wc: {}", why.desc),
        Ok(process) => process,
    };

    {
        // The `stdin` field has type `Option<PipeStream>`
        // `take_unwrap` will take the value wrapped in a `Some` variant
        // Note that we take ownership of `stdin` here
        let mut stdin = process.stdin.take().unwrap();

        // Write a string to the stdin of `wc`
        match stdin.write_str(PANGRAM) {
            Err(why) => panic!("couldn't write to wc stdin: {}", why.desc),
            Ok(_) => println!("sent pangram to wc"),
        }

        // `stdin` gets `drop`ed her, and the pipe is closed
        // This is very important, otherwise `wc` wouldn't start processing the
        // input we just sent
    }

    // The `stdout` field also has type `Option<PipeStream>`
    // the `get_mut_ref` method will return a mutable reference to the value
    // wrapped in a `Some` variant
    match process.stdout.as_mut().unwrap().read_to_string() {
        Err(why) => panic!("couldn't read wc stdout: {}", why.desc),
        Ok(string) => print!("wc responded with:\n{}", string),
    }
}
<commit_msg>Pipes example: update comments<commit_after>use std::io::process::Command;

static PANGRAM: &'static str =
"the quick brown fox jumped over the lazy dog\n";

fn main() {
    // Spawn the `wc` command
    let mut process = match Command::new("wc").spawn() {
        Err(why) => panic!("couldn't spawn wc: {}", why.desc),
        Ok(process) => process,
    };

    {
        // The `stdin` field has type `Option<PipeStream>`
        // `take` will take the value out of an `Option`, leaving `None` in its place
        // Note that we take ownership of `stdin` here
        let mut stdin = process.stdin.take().unwrap();

        // Write a string to the stdin of `wc`
        match stdin.write_str(PANGRAM) {
            Err(why) => panic!("couldn't write to wc stdin: {}", why.desc),
            Ok(_) => println!("sent pangram to wc"),
        }

        // `stdin` gets `drop`ed here, and the pipe is closed
        // This is very important, otherwise `wc` wouldn't start processing the
        // input we just sent
    }

    // The `stdout` field also has type `Option<PipeStream>`
    // the `as_mut` method will return a mutable reference to the value
    // wrapped in a `Some` variant
    match process.stdout.as_mut().unwrap().read_to_string() {
        Err(why) => panic!("couldn't read wc stdout: {}", why.desc),
        Ok(string) => print!("wc responded with:\n{}", string),
    }
}
<|endoftext|>


<commit_before>use std::char;
use std::collections::BTreeMap;
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::num::ParseIntError;
use std::path::Path;

#[cfg(test)]
use std::collections::BTreeSet;
#[cfg(test)]
use std::iter::FromIterator;

use fst;
use fst::MapBuilder;
use fst_generator;
use unicode_names2;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Format(err: ParseIntError) {
            from()
            cause(err)
            description("Hex number format error")
        }
        Block(ch: u32) {
            display("Inconsistent block at {}", ch)
        }
        IO(err: io::Error) {
            from()
                cause(err)
                description("Couldn't read from file")
        }
        FST(err: fst::Error) {
            from()
                cause(err)
                description("Something went wrong inserting into the FST")
        }
    }
}

#[derive(PartialEq, Debug)]
enum LineType {
    None,
    Simple,
    BlockStart(u32),
    BlockEnd(u32),
}

fn process_line(names: &mut fst_generator::Names, line: &str) -> Result<LineType, Error> {
    if line.starts_with('#') || line.trim_left() == "" {
        return Ok(LineType::None);
    }
    let fields: Vec<&str> = line.splitn(15, ';').collect();
    let cp = try!(u32::from_str_radix(fields[0], 16));
    if let Some(ch) = char::from_u32(cp) {
        let query = fields[1].to_owned();
        if query.ends_with(", First>") {
            return Ok(LineType::BlockStart(ch as u32));
        } else if query.ends_with(", Last>") {
            return Ok(LineType::BlockEnd(ch as u32));
        }
        names.insert(vec![query], ch);
        // TODO: Add the unicode_names2 name here
        match fields.get(10) {
            Some(&"") | None => {}
            Some(name) => names.insert(vec![name.to_string()], ch),
        }
        Ok(LineType::Simple)
    } else {
        Ok(LineType::None)
    }
}

#[test]
fn test_processing() {
    {
        let mut sorted_names = fst_generator::Names::new();

        // Non-data gets skipped:
        assert_eq!(
            LineType::None,
            process_line(&mut sorted_names, "# this is a comment").unwrap()
        );
        assert_eq!(LineType::None, process_line(&mut sorted_names, "").unwrap());
        assert_eq!(
            LineType::None,
            process_line(&mut sorted_names, "    ").unwrap()
        );
    }
    {
        let mut sorted_names = fst_generator::Names::new();
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B"
            ).unwrap()
        );
        let have = BTreeSet::from_iter(sorted_names.iter().map(|(name, chs)| {
            let v: Vec<char> = chs.into_iter().map(|ch| ch.to_owned()).collect();
            (name.as_str(), v)
        }));
        let want = BTreeSet::from_iter(vec![
            // Current unicode spelling:
            ("greek", vec!['\u{03bb}']),
            ("greek small letter lamda", vec!['\u{03bb}']),
            ("lamda", vec!['\u{03bb}']),
            // Unicode 1.0 spelling:
            ("greek small letter lambda", vec!['\u{03bb}']),
            ("lambda", vec!['\u{03bb}']),
        ]);
        assert_eq!(have, want);
    }

    {
        let mut sorted_names = fst_generator::Names::new();
        // Some from NameAliases.txt:
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0091;PRIVATE USE ONE;control").unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0092;PRIVATE USE TWO;control").unwrap()
        );

        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0005;ENQUIRY;control").unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "200D;ZWJ;abbreviation").unwrap()
        );

        // And some from UnicodeData.txt:
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;"
            ).unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 \
                 030F;;;;N;;;;0215;e"
            ).unwrap()
        );

        // CJK blocks:
        assert_eq!(
            LineType::BlockStart(0x3400),
            process_line(
                &mut sorted_names,
                "3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;"
            ).unwrap()
        );
        assert_eq!(
            LineType::BlockEnd(0x4DB5),
            process_line(
                &mut sorted_names,
                "4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;"
            ).unwrap()
        );

        let have = BTreeSet::from_iter(sorted_names.iter().map(|(name, chs)| {
            let v: Vec<char> = chs.into_iter().map(|ch| ch.to_owned()).collect();
            (name.as_str(), v)
        }));
        let want = BTreeSet::from_iter(vec![
            ("capital", vec!['\u{0214}']),
            ("double", vec!['\u{0214}']),
            ("enquiry", vec!['\u{0005}']),
            ("grave", vec!['\u{0214}']),
            ("latin", vec!['\u{0214}']),
            ("latin capital letter u with double grave", vec!['\u{0214}']),
            ("one", vec!['\u{91}']),
            ("two", vec!['\u{92}']),
            ("private", vec!['\u{91}', '\u{92}']),
            ("use", vec!['\u{91}', '\u{92}']),
            ("private use one", vec!['\u{91}']),
            ("private use two", vec!['\u{92}']),
            ("registered", vec!['\u{AE}']),
            ("trade", vec!['\u{AE}']),
            ("mark", vec!['\u{AE}']),
            ("registered sign", vec!['\u{AE}']),
            ("registered trade mark sign", vec!['\u{AE}']),
            // Skip the "U": it's too short to be meaningful
            ("zwj", vec!['\u{200D}']),
        ]);
        assert_eq!(have, want);
    }
}

#[test]
fn test_old_names() {}

pub fn read_names(names: &mut fst_generator::Names, file: &Path) -> Result<(), Error> {
    let reader = BufReader::new(try!(File::open(file)));
    let mut lines = reader.lines();
    while let Some(line) = lines.next() {
        match try!(process_line(names, try!(line).as_str())) {
            LineType::Simple | LineType::None => {}
            LineType::BlockStart(start) => {
                let line = lines.next().ok_or(Error::Block(start))??;
                match try!(process_line(names, &line)) {
                    LineType::Simple | LineType::None | LineType::BlockStart(_) => {
                        return Err(Error::Block(start))
                    }
                    // TODO: update to inclusive range syntax when it's stable:
                    LineType::BlockEnd(end) => for i in start..end + 1 {
                        if let Some(ch) = char::from_u32(i as u32) {
                            if let Some(name) = unicode_names2::name(ch) {
                                names.insert(vec![name.to_string()], ch);
                            }
                        }
                    },
                }
            }
            LineType::BlockEnd(end) => {
                return Err(Error::Block(end));
            }
        }
    }
    Ok(())
}

pub fn write_name_data(names: &fst_generator::Names, output: &Path) -> Result<(), Error> {
    let fst_byte_filename = output.join("name_fst.bin");
    let out = BufWriter::new(try!(File::create(fst_byte_filename)));
    let mut map_builder = try!(MapBuilder::new(out));

    let mut counter: u64 = 0;
    let mut results: BTreeMap<String, u64> = BTreeMap::new();

    for (name, chs) in names.iter() {
        if chs.len() > 1 {
            let mut key = String::new();
            for c in chs {
                key.push(*c)
            }

            let num: u64 = (0xff << 32) | *results.entry(key).or_insert_with(|| {
                counter += 1;
                counter - 1
            });
            try!(map_builder.insert(name, num));
        } else {
            try!(map_builder.insert(name, *chs.iter().next().unwrap() as u64));
        }
    }
    try!(map_builder.finish());

    // Now generate the multi-results file:
    let multi_result_filename = output.join("names.rs");
    let mut rust_out = BufWriter::new(try!(File::create(multi_result_filename)));
    let mut ambiguous_chars: Vec<String> = vec![String::new(); counter as usize];
    for (chars, i) in results {
        ambiguous_chars[i as usize] = chars;
    }
    try!(writeln!(&mut rust_out, "/// Generated with `make names`"));
    try!(writeln!(
        &mut rust_out,
        "#[cfg_attr(rustfmt, rustfmt_skip)]"
    ));
    try!(writeln!(
        &mut rust_out,
        "pub static AMBIGUOUS_CHARS: &'static [&'static str; {}] = &[",
        ambiguous_chars.len()
    ));
    for chars in ambiguous_chars {
        try!(writeln!(&mut rust_out, "    {:?},", chars));
    }
    try!(writeln!(&mut rust_out, "];"));

    Ok(())
}
<commit_msg>Remove TODO comment<commit_after>use std::char;
use std::collections::BTreeMap;
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::num::ParseIntError;
use std::path::Path;

#[cfg(test)]
use std::collections::BTreeSet;
#[cfg(test)]
use std::iter::FromIterator;

use fst;
use fst::MapBuilder;
use fst_generator;
use unicode_names2;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Format(err: ParseIntError) {
            from()
            cause(err)
            description("Hex number format error")
        }
        Block(ch: u32) {
            display("Inconsistent block at {}", ch)
        }
        IO(err: io::Error) {
            from()
                cause(err)
                description("Couldn't read from file")
        }
        FST(err: fst::Error) {
            from()
                cause(err)
                description("Something went wrong inserting into the FST")
        }
    }
}

#[derive(PartialEq, Debug)]
enum LineType {
    None,
    Simple,
    BlockStart(u32),
    BlockEnd(u32),
}

fn process_line(names: &mut fst_generator::Names, line: &str) -> Result<LineType, Error> {
    if line.starts_with('#') || line.trim_left() == "" {
        return Ok(LineType::None);
    }
    let fields: Vec<&str> = line.splitn(15, ';').collect();
    let cp = try!(u32::from_str_radix(fields[0], 16));
    if let Some(ch) = char::from_u32(cp) {
        let query = fields[1].to_owned();
        if query.ends_with(", First>") {
            return Ok(LineType::BlockStart(ch as u32));
        } else if query.ends_with(", Last>") {
            return Ok(LineType::BlockEnd(ch as u32));
        }
        names.insert(vec![query], ch);
        match fields.get(10) {
            Some(&"") | None => {}
            Some(name) => names.insert(vec![name.to_string()], ch),
        }
        Ok(LineType::Simple)
    } else {
        Ok(LineType::None)
    }
}

#[test]
fn test_processing() {
    {
        let mut sorted_names = fst_generator::Names::new();

        // Non-data gets skipped:
        assert_eq!(
            LineType::None,
            process_line(&mut sorted_names, "# this is a comment").unwrap()
        );
        assert_eq!(LineType::None, process_line(&mut sorted_names, "").unwrap());
        assert_eq!(
            LineType::None,
            process_line(&mut sorted_names, "    ").unwrap()
        );
    }
    {
        let mut sorted_names = fst_generator::Names::new();
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B"
            ).unwrap()
        );
        let have = BTreeSet::from_iter(sorted_names.iter().map(|(name, chs)| {
            let v: Vec<char> = chs.into_iter().map(|ch| ch.to_owned()).collect();
            (name.as_str(), v)
        }));
        let want = BTreeSet::from_iter(vec![
            // Current unicode spelling:
            ("greek", vec!['\u{03bb}']),
            ("greek small letter lamda", vec!['\u{03bb}']),
            ("lamda", vec!['\u{03bb}']),
            // Unicode 1.0 spelling:
            ("greek small letter lambda", vec!['\u{03bb}']),
            ("lambda", vec!['\u{03bb}']),
        ]);
        assert_eq!(have, want);
    }

    {
        let mut sorted_names = fst_generator::Names::new();
        // Some from NameAliases.txt:
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0091;PRIVATE USE ONE;control").unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0092;PRIVATE USE TWO;control").unwrap()
        );

        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "0005;ENQUIRY;control").unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(&mut sorted_names, "200D;ZWJ;abbreviation").unwrap()
        );

        // And some from UnicodeData.txt:
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;"
            ).unwrap()
        );
        assert_eq!(
            LineType::Simple,
            process_line(
                &mut sorted_names,
                "0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 \
                 030F;;;;N;;;;0215;e"
            ).unwrap()
        );

        // CJK blocks:
        assert_eq!(
            LineType::BlockStart(0x3400),
            process_line(
                &mut sorted_names,
                "3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;"
            ).unwrap()
        );
        assert_eq!(
            LineType::BlockEnd(0x4DB5),
            process_line(
                &mut sorted_names,
                "4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;"
            ).unwrap()
        );

        let have = BTreeSet::from_iter(sorted_names.iter().map(|(name, chs)| {
            let v: Vec<char> = chs.into_iter().map(|ch| ch.to_owned()).collect();
            (name.as_str(), v)
        }));
        let want = BTreeSet::from_iter(vec![
            ("capital", vec!['\u{0214}']),
            ("double", vec!['\u{0214}']),
            ("enquiry", vec!['\u{0005}']),
            ("grave", vec!['\u{0214}']),
            ("latin", vec!['\u{0214}']),
            ("latin capital letter u with double grave", vec!['\u{0214}']),
            ("one", vec!['\u{91}']),
            ("two", vec!['\u{92}']),
            ("private", vec!['\u{91}', '\u{92}']),
            ("use", vec!['\u{91}', '\u{92}']),
            ("private use one", vec!['\u{91}']),
            ("private use two", vec!['\u{92}']),
            ("registered", vec!['\u{AE}']),
            ("trade", vec!['\u{AE}']),
            ("mark", vec!['\u{AE}']),
            ("registered sign", vec!['\u{AE}']),
            ("registered trade mark sign", vec!['\u{AE}']),
            // Skip the "U": it's too short to be meaningful
            ("zwj", vec!['\u{200D}']),
        ]);
        assert_eq!(have, want);
    }
}

#[test]
fn test_old_names() {}

pub fn read_names(names: &mut fst_generator::Names, file: &Path) -> Result<(), Error> {
    let reader = BufReader::new(try!(File::open(file)));
    let mut lines = reader.lines();
    while let Some(line) = lines.next() {
        match try!(process_line(names, try!(line).as_str())) {
            LineType::Simple | LineType::None => {}
            LineType::BlockStart(start) => {
                let line = lines.next().ok_or(Error::Block(start))??;
                match try!(process_line(names, &line)) {
                    LineType::Simple | LineType::None | LineType::BlockStart(_) => {
                        return Err(Error::Block(start))
                    }
                    // TODO: update to inclusive range syntax when it's stable:
                    LineType::BlockEnd(end) => for i in start..end + 1 {
                        if let Some(ch) = char::from_u32(i as u32) {
                            if let Some(name) = unicode_names2::name(ch) {
                                names.insert(vec![name.to_string()], ch);
                            }
                        }
                    },
                }
            }
            LineType::BlockEnd(end) => {
                return Err(Error::Block(end));
            }
        }
    }
    Ok(())
}

pub fn write_name_data(names: &fst_generator::Names, output: &Path) -> Result<(), Error> {
    let fst_byte_filename = output.join("name_fst.bin");
    let out = BufWriter::new(try!(File::create(fst_byte_filename)));
    let mut map_builder = try!(MapBuilder::new(out));

    let mut counter: u64 = 0;
    let mut results: BTreeMap<String, u64> = BTreeMap::new();

    for (name, chs) in names.iter() {
        if chs.len() > 1 {
            let mut key = String::new();
            for c in chs {
                key.push(*c)
            }

            let num: u64 = (0xff << 32) | *results.entry(key).or_insert_with(|| {
                counter += 1;
                counter - 1
            });
            try!(map_builder.insert(name, num));
        } else {
            try!(map_builder.insert(name, *chs.iter().next().unwrap() as u64));
        }
    }
    try!(map_builder.finish());

    // Now generate the multi-results file:
    let multi_result_filename = output.join("names.rs");
    let mut rust_out = BufWriter::new(try!(File::create(multi_result_filename)));
    let mut ambiguous_chars: Vec<String> = vec![String::new(); counter as usize];
    for (chars, i) in results {
        ambiguous_chars[i as usize] = chars;
    }
    try!(writeln!(&mut rust_out, "/// Generated with `make names`"));
    try!(writeln!(
        &mut rust_out,
        "#[cfg_attr(rustfmt, rustfmt_skip)]"
    ));
    try!(writeln!(
        &mut rust_out,
        "pub static AMBIGUOUS_CHARS: &'static [&'static str; {}] = &[",
        ambiguous_chars.len()
    ));
    for chars in ambiguous_chars {
        try!(writeln!(&mut rust_out, "    {:?},", chars));
    }
    try!(writeln!(&mut rust_out, "];"));

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Add get_ptr_to_global and add_global_mapping APIs and its unit tests.<commit_after><|endoftext|>