<commit_before>use std::cell::RefCell;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Write};
use std::net::SocketAddr;
use std::rc::Rc;
use std::time::{Duration, Instant};

use bytes::Bytes;
use bytes::buf::FromBuf;
use futures::{Async, Future, Poll};
use futures::task::{self, Task};
use kcp::{Error as KcpError, Kcp, KcpResult, get_conv};
use tokio_core::net::UdpSocket;
use tokio_core::reactor::Handle;

use config::KcpConfig;

struct KcpOutputInner {
    udp: Rc<UdpSocket>,
    task: Option<Task>,
    pkt_queue: VecDeque<(SocketAddr, Bytes)>,
    is_finished: bool,
}

impl KcpOutputInner {
    fn new(udp: Rc<UdpSocket>) -> KcpOutputInner {
        KcpOutputInner {
            udp: udp,
            task: None,
            pkt_queue: VecDeque::new(),
            is_finished: false,
        }
    }

    fn notify(&mut self) {
        if let Some(task) = self.task.take() {
            task.notify();
        }
    }

    fn push_packet(&mut self, pkt: Bytes, peer: SocketAddr) {
        self.pkt_queue.push_back((peer, pkt));
        self.notify();
    }

    fn close(&mut self) {
        self.is_finished = true;
        self.notify();
    }

    fn is_empty(&self) -> bool {
        self.pkt_queue.is_empty()
    }

    fn send_or_push(&mut self, buf: &[u8], peer: &SocketAddr) -> io::Result<usize> {
        if self.is_empty() {
            match self.udp.send_to(buf, peer) {
                Ok(n) => {
                    trace!("[SEND] UDP peer={} conv={} size={} {:?}",
                           peer,
                           get_conv(buf),
                           buf.len(),
                           ::debug::BsDebug(buf));
                    if n != buf.len() {
                        error!("[SEND] UDP Sent size={}, but packet is size={}", n, buf.len());
                    }
                    return Ok(n);
                }
                Err(ref err) if err.kind() == ErrorKind::WouldBlock => {}
                Err(err) => return Err(err),
            }
        }

        trace!("[SEND] Delay UDP peer={} conv={} size={} qsize={} {:?}",
               peer,
               get_conv(buf),
               buf.len(),
               self.pkt_queue.len(),
               ::debug::BsDebug(buf));

        self.push_packet(Bytes::from_buf(buf), *peer);
        Ok(buf.len())
    }
}

impl Drop for KcpOutputInner {
    fn drop(&mut self) {
        self.close();
    }
}

struct KcpOutputQueue {
    inner: Rc<RefCell<KcpOutputInner>>,
}

impl Future for KcpOutputQueue {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<(), io::Error> {
        let mut inner = self.inner.borrow_mut();

        while !inner.pkt_queue.is_empty() {
            {
                let &(ref peer, ref pkt) = &inner.pkt_queue[0];
                let n = try_nb!(inner.udp.send_to(&*pkt, peer));
                trace!("[SEND] Delayed UDP peer={} conv={} size={} {:?}", peer, get_conv(&pkt), pkt.len(), pkt);
                if n != pkt.len() {
                    error!("[SEND] Delayed Sent size={}, but packet is size={}", n, pkt.len());
                }
            }

            let _ = inner.pkt_queue.pop_front();
        }

        if inner.is_finished {
            Ok(Async::Ready(()))
        } else {
            inner.task = Some(task::current());
            Ok(Async::NotReady)
        }
    }
}

#[derive(Clone)]
pub struct KcpOutputHandle {
    inner: Rc<RefCell<KcpOutputInner>>,
}

impl KcpOutputHandle {
    pub fn new(udp: Rc<UdpSocket>, handle: &Handle) -> KcpOutputHandle {
        let inner = KcpOutputInner::new(udp);
        let inner = Rc::new(RefCell::new(inner));
        let queue = KcpOutputQueue { inner: inner.clone() };
        handle.spawn(queue.map_err(move |err| {
                                       error!("[SEND] UDP output queue failed, err: {:?}", err);
                                   }));
        KcpOutputHandle { inner: inner }
    }

    fn send_to(&mut self, buf: &[u8], peer: &SocketAddr) -> io::Result<usize> {
        let mut inner = self.inner.borrow_mut();
        inner.send_or_push(buf, peer)
    }

    fn udp(&self) -> Rc<UdpSocket> {
        let inner = self.inner.borrow();
        inner.udp.clone()
    }
}

pub struct KcpOutput {
    inner: KcpOutputHandle,
    peer: SocketAddr,
}

impl KcpOutput {
    pub fn new(udp: Rc<UdpSocket>, peer: SocketAddr, handle: &Handle) -> KcpOutput {
        KcpOutput::new_with_handle(KcpOutputHandle::new(udp, handle), peer)
    }

    pub fn new_with_handle(h: KcpOutputHandle, peer: SocketAddr) -> KcpOutput {
        KcpOutput {
            inner: h,
            peer: peer,
        }
    }

    fn udp(&self) -> Rc<UdpSocket> {
        self.inner.udp()
    }
}

impl Write for KcpOutput {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.send_to(buf, &self.peer)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

struct KcpCell {
    kcp: Kcp<KcpOutput>,
    last_update: Instant,
    is_closed: bool,
    send_task: Option<Task>,
    udp: Rc<UdpSocket>,
    recv_buf: Vec<u8>,
    expired: bool,
    sent_first: bool,
}

impl Drop for KcpCell {
    fn drop(&mut self) {
        let _ = self.kcp.flush();
    }
}

impl KcpCell {
    fn input(&mut self, buf: &[u8]) -> KcpResult<()> {
        match self.kcp.input(buf) {
            Ok(..) => {}
            Err(KcpError::ConvInconsistent(expected, actual)) => {
                trace!("[INPUT] Conv expected={} actual={} ignored", expected, actual);
                return Ok(());
            }
            Err(err) => return Err(err),
        }
        // self.kcp.input(buf)?;
        self.last_update = Instant::now();
        Ok(())
    }

    fn input_self(&mut self, n: usize) -> KcpResult<()> {
        match self.kcp.input(&self.recv_buf[..n]) {
            Ok(..) => {}
            Err(KcpError::ConvInconsistent(expected, actual)) => {
                trace!("[INPUT] Conv expected={} actual={} ignored", expected, actual);
                return Ok(());
            }
            Err(err) => return Err(err),
        }
        // self.kcp.input(&self.recv_buf[..n])?;
        self.last_update = Instant::now();
        Ok(())
    }

    fn fetch(&mut self) -> KcpResult<()> {
        if let Async::NotReady = self.udp.poll_read() {
            return Ok(());
        }

        // Initialize with MTU
        if self.recv_buf.len() < self.kcp.mtu() {
            let mtu = self.kcp.mtu();
            self.recv_buf.resize(mtu, 0);
        }

        let n = match self.udp.recv_from(&mut self.recv_buf) {
            Ok((n, _)) => n,
            Err(ref err) if err.kind() == ErrorKind::WouldBlock => {
                return Ok(());
            }
            Err(err) => return Err(From::from(err)),
        };

        // Ah, we got something
        trace!("[RECV] Fetch. SharedKcp recv size={} {:?}", n, ::debug::BsDebug(&self.recv_buf[..n]));
        self.input_self(n)
    }
}

#[derive(Clone)]
pub struct SharedKcp {
    inner: Rc<RefCell<KcpCell>>,
}

impl SharedKcp {
    pub fn new(c: &KcpConfig, conv: u32, udp: Rc<UdpSocket>, peer: SocketAddr, handle: &Handle) -> SharedKcp {
        let output = KcpOutput::new(udp, peer, handle);
        SharedKcp::new_with_output(c, conv, output)
    }

    pub fn new_with_output(c: &KcpConfig, conv: u32, output: KcpOutput) -> SharedKcp {
        let udp = output.udp();
        let mut kcp = Kcp::new(conv, output);
        c.apply_config(&mut kcp);

        // Ask server to allocate one
        if conv == 0 {
            kcp.input_conv();
        }

        SharedKcp {
            inner: Rc::new(RefCell::new(KcpCell {
                                            kcp: kcp,
                                            last_update: Instant::now(),
                                            is_closed: false,
                                            send_task: None,
                                            udp: udp,
                                            recv_buf: Vec::new(), // Do not initialize it yet.
                                            expired: false,
                                            sent_first: false,
                                        })),
        }
    }

    /// Call every time you got data from transmission
    pub fn input(&mut self, buf: &[u8]) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.input(buf)
    }

    /// Recv then input, ignore WouldBlock
    pub fn fetch(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.fetch()
    }

    /// Call if you want to send some data
    pub fn send(&mut self, buf: &[u8]) -> KcpResult<usize> {
        let mut inner = self.inner.borrow_mut();

        // If:
        //     1. Have sent the first packet (asking for conv)
        //     2. Too many pending packets
        if inner.sent_first && (inner.kcp.wait_snd() >= inner.kcp.snd_wnd() as usize || inner.kcp.waiting_conv()) {
            trace!("[SEND] waitsnd={} sndwnd={} excceeded or waiting conv={}",
                   inner.kcp.wait_snd(),
                   inner.kcp.snd_wnd(),
                   inner.kcp.waiting_conv());
            inner.send_task = Some(task::current());
            return Err(From::from(io::Error::new(ErrorKind::WouldBlock, "too many pending packets")));
        }

        let n = inner.kcp.send(buf)?;
        inner.sent_first = true;
        inner.last_update = Instant::now();
        Ok(n)
    }

    /// Try to notify writable
    pub fn try_notify_writable(&mut self) {
        let mut inner = self.inner.borrow_mut();
        if inner.kcp.wait_snd() < inner.kcp.snd_wnd() as usize {
            if let Some(task) = inner.send_task.take() {
                task.notify();
            }
        }
    }

    /// Call if you want to get some data
    /// Always call right after input
    pub fn recv(&mut self, buf: &mut [u8]) -> KcpResult<usize> {
        let mut inner = self.inner.borrow_mut();
        if inner.expired {
            // If it is already expired, return EOF
            return Ok(0);
        }

        let n = inner.kcp.recv(buf)?;
        inner.last_update = Instant::now();
        Ok(n)
    }

    /// Call if you want to flush all pending data in queue
    pub fn flush(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.kcp.flush()?;
        inner.last_update = Instant::now();
        Ok(())
    }

    /// Tell me how long this session have no interaction
    pub fn elapsed(&self) -> Duration {
        let inner = self.inner.borrow();
        inner.last_update.elapsed()
    }

    /// Make this session expire, all read apis will return 0 (EOF)
    /// It will flush the buffer when it is called
    pub fn set_expired(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.expired = true;
        inner.kcp.flush()
    }

    /// Call in every tick
    /// Returns when to call this function again
    pub fn update(&mut self) -> KcpResult<Instant> {
        let mut inner = self.inner.borrow_mut();
        inner.kcp.update(::current())?;
        let next = inner.kcp.check(::current());
        Ok(Instant::now() + Duration::from_millis(next as u64))
    }

    /// Check if send queue is empty
    pub fn has_waitsnd(&self) -> bool {
        let inner = self.inner.borrow();
        inner.kcp.wait_snd() != 0
    }

    /// Get mtu
    pub fn mtu(&self) -> usize {
        let inner = self.inner.borrow();
        inner.kcp.mtu()
    }

    /// Set is close
    pub fn close(&mut self) {
        let mut inner = self.inner.borrow_mut();
        inner.is_closed = true;
    }

    /// Check if it is closed
    pub fn is_closed(&self) -> bool {
        let inner = self.inner.borrow();
        inner.is_closed
    }

    /// Check if it can read
    pub fn can_read(&self) -> bool {
        let inner = self.inner.borrow();
        inner.kcp.peeksize().unwrap_or(0) != 0
    }

    /// Peek
    pub fn peeksize(&self) -> usize {
        let inner = self.inner.borrow();
        inner.kcp.peeksize().unwrap_or(0)
    }

    /// Check if waitsnd > snd_wnd
    pub fn can_send(&self) -> bool {
        let inner = self.inner.borrow();
        inner.kcp.wait_snd() < inner.kcp.snd_wnd() as usize && !inner.kcp.waiting_conv()
    }

    /// Get conv
    pub fn conv(&self) -> u32 {
        let inner = self.inner.borrow();
        inner.kcp.conv()
    }
}
<commit_msg>Fixed bug, notify writable after having received the first input<commit_after>use std::cell::RefCell;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Write};
use std::net::SocketAddr;
use std::rc::Rc;
use std::time::{Duration, Instant};

use bytes::Bytes;
use bytes::buf::FromBuf;
use futures::{Async, Future, Poll};
use futures::task::{self, Task};
use kcp::{Error as KcpError, Kcp, KcpResult, get_conv};
use tokio_core::net::UdpSocket;
use tokio_core::reactor::Handle;

use config::KcpConfig;

struct KcpOutputInner {
    udp: Rc<UdpSocket>,
    task: Option<Task>,
    pkt_queue: VecDeque<(SocketAddr, Bytes)>,
    is_finished: bool,
}

impl KcpOutputInner {
    fn new(udp: Rc<UdpSocket>) -> KcpOutputInner {
        KcpOutputInner {
            udp: udp,
            task: None,
            pkt_queue: VecDeque::new(),
            is_finished: false,
        }
    }

    fn notify(&mut self) {
        if let Some(task) = self.task.take() {
            task.notify();
        }
    }

    fn push_packet(&mut self, pkt: Bytes, peer: SocketAddr) {
        self.pkt_queue.push_back((peer, pkt));
        self.notify();
    }

    fn close(&mut self) {
        self.is_finished = true;
        self.notify();
    }

    fn is_empty(&self) -> bool {
        self.pkt_queue.is_empty()
    }

    fn send_or_push(&mut self, buf: &[u8], peer: &SocketAddr) -> io::Result<usize> {
        if self.is_empty() {
            match self.udp.send_to(buf, peer) {
                Ok(n) => {
                    trace!("[SEND] UDP peer={} conv={} size={} {:?}",
                           peer,
                           get_conv(buf),
                           buf.len(),
                           ::debug::BsDebug(buf));
                    if n != buf.len() {
                        error!("[SEND] UDP Sent size={}, but packet is size={}", n, buf.len());
                    }
                    return Ok(n);
                }
                Err(ref err) if err.kind() == ErrorKind::WouldBlock => {}
                Err(err) => return Err(err),
            }
        }

        trace!("[SEND] Delay UDP peer={} conv={} size={} qsize={} {:?}",
               peer,
               get_conv(buf),
               buf.len(),
               self.pkt_queue.len(),
               ::debug::BsDebug(buf));

        self.push_packet(Bytes::from_buf(buf), *peer);
        Ok(buf.len())
    }
}

impl Drop for KcpOutputInner {
    fn drop(&mut self) {
        self.close();
    }
}

struct KcpOutputQueue {
    inner: Rc<RefCell<KcpOutputInner>>,
}

impl Future for KcpOutputQueue {
    type Item = ();
    type Error = io::Error;

    fn poll(&mut self) -> Poll<(), io::Error> {
        let mut inner = self.inner.borrow_mut();

        while !inner.pkt_queue.is_empty() {
            {
                let &(ref peer, ref pkt) = &inner.pkt_queue[0];
                let n = try_nb!(inner.udp.send_to(&*pkt, peer));
                trace!("[SEND] Delayed UDP peer={} conv={} size={} {:?}", peer, get_conv(&pkt), pkt.len(), pkt);
                if n != pkt.len() {
                    error!("[SEND] Delayed Sent size={}, but packet is size={}", n, pkt.len());
                }
            }

            let _ = inner.pkt_queue.pop_front();
        }

        if inner.is_finished {
            Ok(Async::Ready(()))
        } else {
            inner.task = Some(task::current());
            Ok(Async::NotReady)
        }
    }
}

#[derive(Clone)]
pub struct KcpOutputHandle {
    inner: Rc<RefCell<KcpOutputInner>>,
}

impl KcpOutputHandle {
    pub fn new(udp: Rc<UdpSocket>, handle: &Handle) -> KcpOutputHandle {
        let inner = KcpOutputInner::new(udp);
        let inner = Rc::new(RefCell::new(inner));
        let queue = KcpOutputQueue { inner: inner.clone() };
        handle.spawn(queue.map_err(move |err| {
                                       error!("[SEND] UDP output queue failed, err: {:?}", err);
                                   }));
        KcpOutputHandle { inner: inner }
    }

    fn send_to(&mut self, buf: &[u8], peer: &SocketAddr) -> io::Result<usize> {
        let mut inner = self.inner.borrow_mut();
        inner.send_or_push(buf, peer)
    }

    fn udp(&self) -> Rc<UdpSocket> {
        let inner = self.inner.borrow();
        inner.udp.clone()
    }
}

pub struct KcpOutput {
    inner: KcpOutputHandle,
    peer: SocketAddr,
}

impl KcpOutput {
    pub fn new(udp: Rc<UdpSocket>, peer: SocketAddr, handle: &Handle) -> KcpOutput {
        KcpOutput::new_with_handle(KcpOutputHandle::new(udp, handle), peer)
    }

    pub fn new_with_handle(h: KcpOutputHandle, peer: SocketAddr) -> KcpOutput {
        KcpOutput {
            inner: h,
            peer: peer,
        }
    }

    fn udp(&self) -> Rc<UdpSocket> {
        self.inner.udp()
    }
}

impl Write for KcpOutput {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.send_to(buf, &self.peer)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

struct KcpCell {
    kcp: Kcp<KcpOutput>,
    last_update: Instant,
    is_closed: bool,
    send_task: Option<Task>,
    udp: Rc<UdpSocket>,
    recv_buf: Vec<u8>,
    expired: bool,
    sent_first: bool,
}

impl Drop for KcpCell {
    fn drop(&mut self) {
        let _ = self.kcp.flush();
    }
}

impl KcpCell {
    fn input(&mut self, buf: &[u8]) -> KcpResult<()> {
        match self.kcp.input(buf) {
            Ok(..) => {}
            Err(KcpError::ConvInconsistent(expected, actual)) => {
                trace!("[INPUT] Conv expected={} actual={} ignored", expected, actual);
                return Ok(());
            }
            Err(err) => return Err(err),
        }
        // self.kcp.input(buf)?;
        self.last_update = Instant::now();
        Ok(())
    }

    fn input_self(&mut self, n: usize) -> KcpResult<()> {
        match self.kcp.input(&self.recv_buf[..n]) {
            Ok(..) => {}
            Err(KcpError::ConvInconsistent(expected, actual)) => {
                trace!("[INPUT] Conv expected={} actual={} ignored", expected, actual);
                return Ok(());
            }
            Err(err) => return Err(err),
        }
        // self.kcp.input(&self.recv_buf[..n])?;
        self.last_update = Instant::now();
        Ok(())
    }

    fn fetch(&mut self) -> KcpResult<()> {
        if let Async::NotReady = self.udp.poll_read() {
            return Ok(());
        }

        // Initialize with MTU
        if self.recv_buf.len() < self.kcp.mtu() {
            let mtu = self.kcp.mtu();
            self.recv_buf.resize(mtu, 0);
        }

        let n = match self.udp.recv_from(&mut self.recv_buf) {
            Ok((n, _)) => n,
            Err(ref err) if err.kind() == ErrorKind::WouldBlock => {
                return Ok(());
            }
            Err(err) => return Err(From::from(err)),
        };

        // Ah, we got something
        trace!("[RECV] Fetch. SharedKcp recv size={} {:?}", n, ::debug::BsDebug(&self.recv_buf[..n]));
        self.input_self(n)
    }
}

#[derive(Clone)]
pub struct SharedKcp {
    inner: Rc<RefCell<KcpCell>>,
}

impl SharedKcp {
    pub fn new(c: &KcpConfig, conv: u32, udp: Rc<UdpSocket>, peer: SocketAddr, handle: &Handle) -> SharedKcp {
        let output = KcpOutput::new(udp, peer, handle);
        SharedKcp::new_with_output(c, conv, output)
    }

    pub fn new_with_output(c: &KcpConfig, conv: u32, output: KcpOutput) -> SharedKcp {
        let udp = output.udp();
        let mut kcp = Kcp::new(conv, output);
        c.apply_config(&mut kcp);

        // Ask server to allocate one
        if conv == 0 {
            kcp.input_conv();
        }

        SharedKcp {
            inner: Rc::new(RefCell::new(KcpCell {
                                            kcp: kcp,
                                            last_update: Instant::now(),
                                            is_closed: false,
                                            send_task: None,
                                            udp: udp,
                                            recv_buf: Vec::new(), // Do not initialize it yet.
                                            expired: false,
                                            sent_first: false,
                                        })),
        }
    }

    /// Call every time you got data from transmission
    pub fn input(&mut self, buf: &[u8]) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.input(buf)
    }

    /// Recv then input, ignore WouldBlock
    pub fn fetch(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.fetch()
    }

    /// Call if you want to send some data
    pub fn send(&mut self, buf: &[u8]) -> KcpResult<usize> {
        let mut inner = self.inner.borrow_mut();

        // If:
        //     1. Have sent the first packet (asking for conv)
        //     2. Too many pending packets
        if inner.sent_first && (inner.kcp.wait_snd() >= inner.kcp.snd_wnd() as usize || inner.kcp.waiting_conv()) {
            trace!("[SEND] waitsnd={} sndwnd={} excceeded or waiting conv={}",
                   inner.kcp.wait_snd(),
                   inner.kcp.snd_wnd(),
                   inner.kcp.waiting_conv());
            inner.send_task = Some(task::current());
            return Err(From::from(io::Error::new(ErrorKind::WouldBlock, "too many pending packets")));
        }

        let n = inner.kcp.send(buf)?;
        inner.sent_first = true;
        inner.last_update = Instant::now();
        Ok(n)
    }

    /// Try to notify writable
    pub fn try_notify_writable(&mut self) {
        let mut inner = self.inner.borrow_mut();
        if inner.kcp.wait_snd() < inner.kcp.snd_wnd() as usize && !inner.kcp.waiting_conv() {
            if let Some(task) = inner.send_task.take() {
                task.notify();
            }
        }
    }

    /// Call if you want to get some data
    /// Always call right after input
    pub fn recv(&mut self, buf: &mut [u8]) -> KcpResult<usize> {
        let mut inner = self.inner.borrow_mut();
        if inner.expired {
            // If it is already expired, return EOF
            return Ok(0);
        }

        let n = inner.kcp.recv(buf)?;
        inner.last_update = Instant::now();
        Ok(n)
    }

    /// Call if you want to flush all pending data in queue
    pub fn flush(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.kcp.flush()?;
        inner.last_update = Instant::now();
        Ok(())
    }

    /// Tell me how long this session have no interaction
    pub fn elapsed(&self) -> Duration {
        let inner = self.inner.borrow();
        inner.last_update.elapsed()
    }

    /// Make this session expire, all read apis will return 0 (EOF)
    /// It will flush the buffer when it is called
    pub fn set_expired(&mut self) -> KcpResult<()> {
        let mut inner = self.inner.borrow_mut();
        inner.expired = true;
        inner.kcp.flush()
    }

    /// Call in every tick
    /// Returns when to call this function again
    pub fn update(&mut self) -> KcpResult<Instant> {
        let mut inner = self.inner.borrow_mut();
        inner.kcp.update(::current())?;
        let next = inner.kcp.check(::current());
        Ok(Instant::now() + Duration::from_millis(next as u64))
    }

    /// Check if send queue is empty
    pub fn has_waitsnd(&self) -> bool {
        let inner = self.inner.borrow();
        inner.kcp.wait_snd() != 0
    }

    /// Get mtu
    pub fn mtu(&self) -> usize {
        let inner = self.inner.borrow();
        inner.kcp.mtu()
    }

    /// Set is close
    pub fn close(&mut self) {
        let mut inner = self.inner.borrow_mut();
        inner.is_closed = true;
    }

    /// Check if it is closed
    pub fn is_closed(&self) -> bool {
        let inner = self.inner.borrow();
        inner.is_closed
    }

    /// Check if it can read
    pub fn can_read(&self) -> bool {
        let inner = self.inner.borrow();
        inner.kcp.peeksize().unwrap_or(0) != 0
    }

    /// Peek
    pub fn peeksize(&self) -> usize {
        let inner = self.inner.borrow();
        inner.kcp.peeksize().unwrap_or(0)
    }

    /// Get conv
    pub fn conv(&self) -> u32 {
        let inner = self.inner.borrow();
        inner.kcp.conv()
    }
}
<|endoftext|>


<commit_before>use nom::{IResult, Err, ErrorKind, Needed};

use std::str;

named!(keyword_follow_char<&[u8], char>,
       peek!(one_of!(" \n;(\t,="))
);

named!(keyword_a_to_c<&[u8], &[u8]>,
       alt_complete!(
          terminated!(caseless_tag!("ABORT"), keyword_follow_char)
        | terminated!(caseless_tag!("ACTION"), keyword_follow_char)
        | terminated!(caseless_tag!("ADD"), keyword_follow_char)
        | terminated!(caseless_tag!("AFTER"), keyword_follow_char)
        | terminated!(caseless_tag!("ALL"), keyword_follow_char)
        | terminated!(caseless_tag!("ALTER"), keyword_follow_char)
        | terminated!(caseless_tag!("ANALYZE"), keyword_follow_char)
        | terminated!(caseless_tag!("AND"), keyword_follow_char)
        | terminated!(caseless_tag!("AS"), keyword_follow_char)
        | terminated!(caseless_tag!("ASC"), keyword_follow_char)
        | terminated!(caseless_tag!("ATTACH"), keyword_follow_char)
        | terminated!(caseless_tag!("AUTOINCREMENT"), keyword_follow_char)
        | terminated!(caseless_tag!("BEFORE"), keyword_follow_char)
        | terminated!(caseless_tag!("BEGIN"), keyword_follow_char)
        | terminated!(caseless_tag!("BETWEEN"), keyword_follow_char)
        | terminated!(caseless_tag!("BY"), keyword_follow_char)
        | terminated!(caseless_tag!("CASCADE"), keyword_follow_char)
        | terminated!(caseless_tag!("CASE"), keyword_follow_char)
        | terminated!(caseless_tag!("CAST"), keyword_follow_char)
        | terminated!(caseless_tag!("CHECK"), keyword_follow_char)
        | terminated!(caseless_tag!("COLLATE"), keyword_follow_char)
        | terminated!(caseless_tag!("COLUMN"), keyword_follow_char)
        | terminated!(caseless_tag!("COMMIT"), keyword_follow_char)
        | terminated!(caseless_tag!("CONFLICT"), keyword_follow_char)
        | terminated!(caseless_tag!("CONSTRAINT"), keyword_follow_char)
        | terminated!(caseless_tag!("CREATE"), keyword_follow_char)
        | terminated!(caseless_tag!("CROSS"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_DATE"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_TIME"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_TIMESTAMP"), keyword_follow_char)
    )
);

named!(keyword_d_to_i<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("DATABASE"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFAULT"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFERRABLE"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFERRED"), keyword_follow_char)
        | terminated!(caseless_tag!("DELETE"), keyword_follow_char)
        | terminated!(caseless_tag!("DESC"), keyword_follow_char)
        | terminated!(caseless_tag!("DETACH"), keyword_follow_char)
        | terminated!(caseless_tag!("DISTINCT"), keyword_follow_char)
        | terminated!(caseless_tag!("DROP"), keyword_follow_char)
        | terminated!(caseless_tag!("EACH"), keyword_follow_char)
        | terminated!(caseless_tag!("ELSE"), keyword_follow_char)
        | terminated!(caseless_tag!("END"), keyword_follow_char)
        | terminated!(caseless_tag!("ESCAPE"), keyword_follow_char)
        | terminated!(caseless_tag!("EXCEPT"), keyword_follow_char)
        | terminated!(caseless_tag!("EXCLUSIVE"), keyword_follow_char)
        | terminated!(caseless_tag!("EXISTS"), keyword_follow_char)
        | terminated!(caseless_tag!("EXPLAIN"), keyword_follow_char)
        | terminated!(caseless_tag!("FAIL"), keyword_follow_char)
        | terminated!(caseless_tag!("FOR"), keyword_follow_char)
        | terminated!(caseless_tag!("FOREIGN"), keyword_follow_char)
        | terminated!(caseless_tag!("FROM"), keyword_follow_char)
        | terminated!(caseless_tag!("FULL"), keyword_follow_char)
        | terminated!(caseless_tag!("GLOB"), keyword_follow_char)
        | terminated!(caseless_tag!("GROUP"), keyword_follow_char)
        | terminated!(caseless_tag!("HAVING"), keyword_follow_char)
        | terminated!(caseless_tag!("IF"), keyword_follow_char)
        | terminated!(caseless_tag!("IGNORE"), keyword_follow_char)
        | terminated!(caseless_tag!("IMMEDIATE"), keyword_follow_char)
        | terminated!(caseless_tag!("IN"), keyword_follow_char)
        | terminated!(caseless_tag!("INDEX"), keyword_follow_char)
        | terminated!(caseless_tag!("INDEXED"), keyword_follow_char)
        | terminated!(caseless_tag!("INITIALLY"), keyword_follow_char)
        | terminated!(caseless_tag!("INNER"), keyword_follow_char)
        | terminated!(caseless_tag!("INSERT"), keyword_follow_char)
        | terminated!(caseless_tag!("INSTEAD"), keyword_follow_char)
        | terminated!(caseless_tag!("INTERSECT"), keyword_follow_char)
        | terminated!(caseless_tag!("INTO"), keyword_follow_char)
        | terminated!(caseless_tag!("IS"), keyword_follow_char)
        | terminated!(caseless_tag!("ISNULL"), keyword_follow_char)
    )
);

named!(keyword_j_to_s<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("ORDER"), keyword_follow_char)
        | terminated!(caseless_tag!("JOIN"), keyword_follow_char)
        | terminated!(caseless_tag!("KEY"), keyword_follow_char)
        | terminated!(caseless_tag!("LEFT"), keyword_follow_char)
        | terminated!(caseless_tag!("LIKE"), keyword_follow_char)
        | terminated!(caseless_tag!("LIMIT"), keyword_follow_char)
        | terminated!(caseless_tag!("MATCH"), keyword_follow_char)
        | terminated!(caseless_tag!("NATURAL"), keyword_follow_char)
        | terminated!(caseless_tag!("NO"), keyword_follow_char)
        | terminated!(caseless_tag!("NOT"), keyword_follow_char)
        | terminated!(caseless_tag!("NOTNULL"), keyword_follow_char)
        | terminated!(caseless_tag!("NULL"), keyword_follow_char)
        | terminated!(caseless_tag!("OF"), keyword_follow_char)
        | terminated!(caseless_tag!("OFFSET"), keyword_follow_char)
        | terminated!(caseless_tag!("ON"), keyword_follow_char)
        | terminated!(caseless_tag!("OR"), keyword_follow_char)
        | terminated!(caseless_tag!("OUTER"), keyword_follow_char)
        | terminated!(caseless_tag!("PLAN"), keyword_follow_char)
        | terminated!(caseless_tag!("PRAGMA"), keyword_follow_char)
        | terminated!(caseless_tag!("PRIMARY"), keyword_follow_char)
        | terminated!(caseless_tag!("QUERY"), keyword_follow_char)
        | terminated!(caseless_tag!("RAISE"), keyword_follow_char)
        | terminated!(caseless_tag!("RECURSIVE"), keyword_follow_char)
        | terminated!(caseless_tag!("REFERENCES"), keyword_follow_char)
        | terminated!(caseless_tag!("REGEXP"), keyword_follow_char)
        | terminated!(caseless_tag!("REINDEX"), keyword_follow_char)
        | terminated!(caseless_tag!("RELEASE"), keyword_follow_char)
        | terminated!(caseless_tag!("RENAME"), keyword_follow_char)
        | terminated!(caseless_tag!("REPLACE"), keyword_follow_char)
        | terminated!(caseless_tag!("RESTRICT"), keyword_follow_char)
        | terminated!(caseless_tag!("RIGHT"), keyword_follow_char)
        | terminated!(caseless_tag!("ROLLBACK"), keyword_follow_char)
        | terminated!(caseless_tag!("ROW"), keyword_follow_char)
        | terminated!(caseless_tag!("SAVEPOINT"), keyword_follow_char)
        | terminated!(caseless_tag!("SELECT"), keyword_follow_char)
        | terminated!(caseless_tag!("SET"), keyword_follow_char)
    )
);

named!(keyword_t_to_z<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("TABLE"), keyword_follow_char)
        | terminated!(caseless_tag!("TEMP"), keyword_follow_char)
        | terminated!(caseless_tag!("TEMPORARY"), keyword_follow_char)
        | terminated!(caseless_tag!("THEN"), keyword_follow_char)
        | terminated!(caseless_tag!("TO"), keyword_follow_char)
        | terminated!(caseless_tag!("TRANSACTION"), keyword_follow_char)
        | terminated!(caseless_tag!("TRIGGER"), keyword_follow_char)
        | terminated!(caseless_tag!("UNION"), keyword_follow_char)
        | terminated!(caseless_tag!("UNIQUE"), keyword_follow_char)
        | terminated!(caseless_tag!("UPDATE"), keyword_follow_char)
        | terminated!(caseless_tag!("USING"), keyword_follow_char)
        | terminated!(caseless_tag!("VACUUM"), keyword_follow_char)
        | terminated!(caseless_tag!("VALUES"), keyword_follow_char)
        | terminated!(caseless_tag!("VIEW"), keyword_follow_char)
        | terminated!(caseless_tag!("VIRTUAL"), keyword_follow_char)
        | terminated!(caseless_tag!("WHEN"), keyword_follow_char)
        | terminated!(caseless_tag!("WHERE"), keyword_follow_char)
        | terminated!(caseless_tag!("WITH"), keyword_follow_char)
        | terminated!(caseless_tag!("WITHOUT"), keyword_follow_char)
    )
);

/// Matches any SQL reserved keyword
named!(pub sql_keyword<&[u8], &[u8]>,
    complete!(chain!(
        kw: alt_complete!(
              keyword_a_to_c
            | keyword_d_to_i
            | keyword_j_to_s
            | keyword_t_to_z),
        || { kw }
    ))
);
<commit_msg>Keywords can be followed by a closing parenthesis<commit_after>use nom::{IResult, Err, ErrorKind, Needed};

use std::str;

named!(keyword_follow_char<&[u8], char>,
       peek!(one_of!(" \n;()\t,="))
);

named!(keyword_a_to_c<&[u8], &[u8]>,
       alt_complete!(
          terminated!(caseless_tag!("ABORT"), keyword_follow_char)
        | terminated!(caseless_tag!("ACTION"), keyword_follow_char)
        | terminated!(caseless_tag!("ADD"), keyword_follow_char)
        | terminated!(caseless_tag!("AFTER"), keyword_follow_char)
        | terminated!(caseless_tag!("ALL"), keyword_follow_char)
        | terminated!(caseless_tag!("ALTER"), keyword_follow_char)
        | terminated!(caseless_tag!("ANALYZE"), keyword_follow_char)
        | terminated!(caseless_tag!("AND"), keyword_follow_char)
        | terminated!(caseless_tag!("AS"), keyword_follow_char)
        | terminated!(caseless_tag!("ASC"), keyword_follow_char)
        | terminated!(caseless_tag!("ATTACH"), keyword_follow_char)
        | terminated!(caseless_tag!("AUTOINCREMENT"), keyword_follow_char)
        | terminated!(caseless_tag!("BEFORE"), keyword_follow_char)
        | terminated!(caseless_tag!("BEGIN"), keyword_follow_char)
        | terminated!(caseless_tag!("BETWEEN"), keyword_follow_char)
        | terminated!(caseless_tag!("BY"), keyword_follow_char)
        | terminated!(caseless_tag!("CASCADE"), keyword_follow_char)
        | terminated!(caseless_tag!("CASE"), keyword_follow_char)
        | terminated!(caseless_tag!("CAST"), keyword_follow_char)
        | terminated!(caseless_tag!("CHECK"), keyword_follow_char)
        | terminated!(caseless_tag!("COLLATE"), keyword_follow_char)
        | terminated!(caseless_tag!("COLUMN"), keyword_follow_char)
        | terminated!(caseless_tag!("COMMIT"), keyword_follow_char)
        | terminated!(caseless_tag!("CONFLICT"), keyword_follow_char)
        | terminated!(caseless_tag!("CONSTRAINT"), keyword_follow_char)
        | terminated!(caseless_tag!("CREATE"), keyword_follow_char)
        | terminated!(caseless_tag!("CROSS"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_DATE"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_TIME"), keyword_follow_char)
        | terminated!(caseless_tag!("CURRENT_TIMESTAMP"), keyword_follow_char)
    )
);

named!(keyword_d_to_i<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("DATABASE"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFAULT"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFERRABLE"), keyword_follow_char)
        | terminated!(caseless_tag!("DEFERRED"), keyword_follow_char)
        | terminated!(caseless_tag!("DELETE"), keyword_follow_char)
        | terminated!(caseless_tag!("DESC"), keyword_follow_char)
        | terminated!(caseless_tag!("DETACH"), keyword_follow_char)
        | terminated!(caseless_tag!("DISTINCT"), keyword_follow_char)
        | terminated!(caseless_tag!("DROP"), keyword_follow_char)
        | terminated!(caseless_tag!("EACH"), keyword_follow_char)
        | terminated!(caseless_tag!("ELSE"), keyword_follow_char)
        | terminated!(caseless_tag!("END"), keyword_follow_char)
        | terminated!(caseless_tag!("ESCAPE"), keyword_follow_char)
        | terminated!(caseless_tag!("EXCEPT"), keyword_follow_char)
        | terminated!(caseless_tag!("EXCLUSIVE"), keyword_follow_char)
        | terminated!(caseless_tag!("EXISTS"), keyword_follow_char)
        | terminated!(caseless_tag!("EXPLAIN"), keyword_follow_char)
        | terminated!(caseless_tag!("FAIL"), keyword_follow_char)
        | terminated!(caseless_tag!("FOR"), keyword_follow_char)
        | terminated!(caseless_tag!("FOREIGN"), keyword_follow_char)
        | terminated!(caseless_tag!("FROM"), keyword_follow_char)
        | terminated!(caseless_tag!("FULL"), keyword_follow_char)
        | terminated!(caseless_tag!("GLOB"), keyword_follow_char)
        | terminated!(caseless_tag!("GROUP"), keyword_follow_char)
        | terminated!(caseless_tag!("HAVING"), keyword_follow_char)
        | terminated!(caseless_tag!("IF"), keyword_follow_char)
        | terminated!(caseless_tag!("IGNORE"), keyword_follow_char)
        | terminated!(caseless_tag!("IMMEDIATE"), keyword_follow_char)
        | terminated!(caseless_tag!("IN"), keyword_follow_char)
        | terminated!(caseless_tag!("INDEX"), keyword_follow_char)
        | terminated!(caseless_tag!("INDEXED"), keyword_follow_char)
        | terminated!(caseless_tag!("INITIALLY"), keyword_follow_char)
        | terminated!(caseless_tag!("INNER"), keyword_follow_char)
        | terminated!(caseless_tag!("INSERT"), keyword_follow_char)
        | terminated!(caseless_tag!("INSTEAD"), keyword_follow_char)
        | terminated!(caseless_tag!("INTERSECT"), keyword_follow_char)
        | terminated!(caseless_tag!("INTO"), keyword_follow_char)
        | terminated!(caseless_tag!("IS"), keyword_follow_char)
        | terminated!(caseless_tag!("ISNULL"), keyword_follow_char)
    )
);

named!(keyword_j_to_s<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("ORDER"), keyword_follow_char)
        | terminated!(caseless_tag!("JOIN"), keyword_follow_char)
        | terminated!(caseless_tag!("KEY"), keyword_follow_char)
        | terminated!(caseless_tag!("LEFT"), keyword_follow_char)
        | terminated!(caseless_tag!("LIKE"), keyword_follow_char)
        | terminated!(caseless_tag!("LIMIT"), keyword_follow_char)
        | terminated!(caseless_tag!("MATCH"), keyword_follow_char)
        | terminated!(caseless_tag!("NATURAL"), keyword_follow_char)
        | terminated!(caseless_tag!("NO"), keyword_follow_char)
        | terminated!(caseless_tag!("NOT"), keyword_follow_char)
        | terminated!(caseless_tag!("NOTNULL"), keyword_follow_char)
        | terminated!(caseless_tag!("NULL"), keyword_follow_char)
        | terminated!(caseless_tag!("OF"), keyword_follow_char)
        | terminated!(caseless_tag!("OFFSET"), keyword_follow_char)
        | terminated!(caseless_tag!("ON"), keyword_follow_char)
        | terminated!(caseless_tag!("OR"), keyword_follow_char)
        | terminated!(caseless_tag!("OUTER"), keyword_follow_char)
        | terminated!(caseless_tag!("PLAN"), keyword_follow_char)
        | terminated!(caseless_tag!("PRAGMA"), keyword_follow_char)
        | terminated!(caseless_tag!("PRIMARY"), keyword_follow_char)
        | terminated!(caseless_tag!("QUERY"), keyword_follow_char)
        | terminated!(caseless_tag!("RAISE"), keyword_follow_char)
        | terminated!(caseless_tag!("RECURSIVE"), keyword_follow_char)
        | terminated!(caseless_tag!("REFERENCES"), keyword_follow_char)
        | terminated!(caseless_tag!("REGEXP"), keyword_follow_char)
        | terminated!(caseless_tag!("REINDEX"), keyword_follow_char)
        | terminated!(caseless_tag!("RELEASE"), keyword_follow_char)
        | terminated!(caseless_tag!("RENAME"), keyword_follow_char)
        | terminated!(caseless_tag!("REPLACE"), keyword_follow_char)
        | terminated!(caseless_tag!("RESTRICT"), keyword_follow_char)
        | terminated!(caseless_tag!("RIGHT"), keyword_follow_char)
        | terminated!(caseless_tag!("ROLLBACK"), keyword_follow_char)
        | terminated!(caseless_tag!("ROW"), keyword_follow_char)
        | terminated!(caseless_tag!("SAVEPOINT"), keyword_follow_char)
        | terminated!(caseless_tag!("SELECT"), keyword_follow_char)
        | terminated!(caseless_tag!("SET"), keyword_follow_char)
    )
);

named!(keyword_t_to_z<&[u8], &[u8]>,
    alt_complete!(
          terminated!(caseless_tag!("TABLE"), keyword_follow_char)
        | terminated!(caseless_tag!("TEMP"), keyword_follow_char)
        | terminated!(caseless_tag!("TEMPORARY"), keyword_follow_char)
        | terminated!(caseless_tag!("THEN"), keyword_follow_char)
        | terminated!(caseless_tag!("TO"), keyword_follow_char)
        | terminated!(caseless_tag!("TRANSACTION"), keyword_follow_char)
        | terminated!(caseless_tag!("TRIGGER"), keyword_follow_char)
        | terminated!(caseless_tag!("UNION"), keyword_follow_char)
        | terminated!(caseless_tag!("UNIQUE"), keyword_follow_char)
        | terminated!(caseless_tag!("UPDATE"), keyword_follow_char)
        | terminated!(caseless_tag!("USING"), keyword_follow_char)
        | terminated!(caseless_tag!("VACUUM"), keyword_follow_char)
        | terminated!(caseless_tag!("VALUES"), keyword_follow_char)
        | terminated!(caseless_tag!("VIEW"), keyword_follow_char)
        | terminated!(caseless_tag!("VIRTUAL"), keyword_follow_char)
        | terminated!(caseless_tag!("WHEN"), keyword_follow_char)
        | terminated!(caseless_tag!("WHERE"), keyword_follow_char)
        | terminated!(caseless_tag!("WITH"), keyword_follow_char)
        | terminated!(caseless_tag!("WITHOUT"), keyword_follow_char)
    )
);

/// Matches any SQL reserved keyword
named!(pub sql_keyword<&[u8], &[u8]>,
    complete!(chain!(
        kw: alt_complete!(
              keyword_a_to_c
            | keyword_d_to_i
            | keyword_j_to_s
            | keyword_t_to_z),
        || { kw }
    ))
);
<|endoftext|>


<commit_before>use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::Path;

#[derive(Deserialize, Debug)]
pub struct User {
    // NOTE: when changing these fields, be sure to update
    // `.remove_empty_strings()` below
    pub name: Option<String>,
    pub irc: Option<String>,
    #[serde(default)]
    pub irc_channels: Vec<String>,
    #[serde(default)]
    pub show_avatar: bool,
    pub email: Option<String>,
    pub discourse: Option<String>,
    pub reddit: Option<String>,
    pub twitter: Option<String>,
    pub blog: Option<String>,
    pub website: Option<String>,
    pub notes: Option<String>,
}

impl User {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<User, serde_json::Error> {
        let reader = BufReader::new(File::open(path)?);
        let mut user: User = serde_json::from_reader(reader)?;
        user.remove_empty_strings();
        Ok(user)
    }

    /// Some users use empty strings to mean "not applicable", instead of using
    /// `null` or omitting the field as they should be doing. We fix up their
    /// carelessness here.
    fn remove_empty_strings(&mut self) {
        macro_rules! fixup {
            ($($field:ident)*) => {
                $(
                    // If the field is just whitespace, replace it with `None`
                    // This boolean dance is needed to satisfy borrowck
                    let should_replace = match self.$field {
                        Some(ref s) if is_whitespace(s) => true,
                        _ => false,
                    };
                    if should_replace {
                        self.$field = None;
                    }
                )*
            }
        }
        fixup!(name irc email discourse reddit twitter blog website notes);
    }
}

fn is_whitespace(s: &str) -> bool {
    s.chars().all(char::is_whitespace)
}

#[derive(Debug)]
pub struct Users {
    data: BTreeMap<String, User>,
}

impl Users {
    pub fn load<P: AsRef<Path>>(base: P) -> Result<Users, serde_json::Error> {
        let mut data = BTreeMap::new();
        for entry in fs::read_dir(base)? {
            let path = entry?.path();
            if path.extension() == Some(OsStr::new("json")) {
                let id = path.file_stem().unwrap().to_string_lossy().into_owned();
                let user = User::from_path(&path)?;
                data.insert(id, user);
            }
        }
        Ok(Users { data: data })
    }

    pub fn get(&self, id: &str) -> Option<&User> {
        self.data.get(id)
    }
}

#[test]
fn smoke() {
    const DATA: &'static str = r#"
        {
            "name": "Bors",
            "show_avatar": true,
            "irc": "bors",
            "irc_channels": ["rust", "rust-bots"],
            "website": "http://rust-lang.org/"
        }
    "#;
    serde_json::from_str(DATA).unwrap();
}
<commit_msg>Unbork tests<commit_after>use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::Path;

#[derive(Deserialize, Debug)]
pub struct User {
    // NOTE: when changing these fields, be sure to update
    // `.remove_empty_strings()` below
    pub name: Option<String>,
    pub irc: Option<String>,
    #[serde(default)]
    pub irc_channels: Vec<String>,
    #[serde(default)]
    pub show_avatar: bool,
    pub email: Option<String>,
    pub discourse: Option<String>,
    pub reddit: Option<String>,
    pub twitter: Option<String>,
    pub blog: Option<String>,
    pub website: Option<String>,
    pub notes: Option<String>,
}

impl User {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<User, serde_json::Error> {
        let reader = BufReader::new(File::open(path)?);
        let mut user: User = serde_json::from_reader(reader)?;
        user.remove_empty_strings();
        Ok(user)
    }

    /// Some users use empty strings to mean "not applicable", instead of using
    /// `null` or omitting the field as they should be doing. We fix up their
    /// carelessness here.
    fn remove_empty_strings(&mut self) {
        macro_rules! fixup {
            ($($field:ident)*) => {
                $(
                    // If the field is just whitespace, replace it with `None`
                    // This boolean dance is needed to satisfy borrowck
                    let should_replace = match self.$field {
                        Some(ref s) if is_whitespace(s) => true,
                        _ => false,
                    };
                    if should_replace {
                        self.$field = None;
                    }
                )*
            }
        }
        fixup!(name irc email discourse reddit twitter blog website notes);
    }
}

fn is_whitespace(s: &str) -> bool {
    s.chars().all(char::is_whitespace)
}

#[derive(Debug)]
pub struct Users {
    data: BTreeMap<String, User>,
}

impl Users {
    pub fn load<P: AsRef<Path>>(base: P) -> Result<Users, serde_json::Error> {
        let mut data = BTreeMap::new();
        for entry in fs::read_dir(base)? {
            let path = entry?.path();
            if path.extension() == Some(OsStr::new("json")) {
                let id = path.file_stem().unwrap().to_string_lossy().into_owned();
                let user = User::from_path(&path)?;
                data.insert(id, user);
            }
        }
        Ok(Users { data: data })
    }

    pub fn get(&self, id: &str) -> Option<&User> {
        self.data.get(id)
    }
}

#[test]
fn smoke() {
    const DATA: &'static str = r#"
        {
            "name": "Bors",
            "show_avatar": true,
            "irc": "bors",
            "irc_channels": ["rust", "rust-bots"],
            "website": "http://rust-lang.org/"
        }
    "#;
    let _: User = serde_json::from_str(DATA).unwrap();
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use escape::escape_html;
use token::Token;
use book::Book;
use number::Number;
use error::{Error,Result};
use toc::Toc;
use resource_handler::ResourceHandler;
use std::borrow::Cow;

use mustache;

/// Renders HTML document.
///
/// Also used by `EpubRenderer`.
pub struct HtmlRenderer<'a> {
    book: &'a Book,
    table_head: bool,
    footnote_number: u32,
    epub3: bool,
    verbatim: bool,
    link_number: u32,

    // fields used by EpubRenderer so marked public but hidden
    #[doc(hidden)]
    pub toc: Toc,
    #[doc(hidden)]
    pub footnotes: Vec<(String, String)>,
    #[doc(hidden)]
    pub current_chapter: [i32;6],
    #[doc(hidden)]
    pub current_numbering: i32,
    #[doc(hidden)]
    pub current_hide: bool,
    #[doc(hidden)]
    pub filename: String,
    #[doc(hidden)]
    pub handler: ResourceHandler,
}

impl<'a> HtmlRenderer<'a> {
    /// Creates a new HTML renderer
    pub fn new(book: &'a Book) -> HtmlRenderer<'a> {
        HtmlRenderer {
            book: book,
            toc: Toc::new(),
            link_number: 0,
            current_chapter: [0, 0, 0, 0, 0, 0],
            current_numbering: book.options.get_i32("numbering").unwrap(),
            current_hide: false,
            table_head: false,
            footnote_number: 0,
            footnotes: vec!(),
            epub3: false,
            verbatim: false,
            filename: String::new(),
            handler: ResourceHandler::new(),
        }
    }

    /// Render books as a standalone HTML file
    pub fn render_book(&mut self) -> Result<String> {
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("#chapter-{}", i));
        }
        let mut content = String::new();

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            content.push_str(&format!("\n", i));
            self.current_hide = false;
            let book_numbering = self.book.options.get_i32("numbering").unwrap();
            match n {
                Number::Unnumbered => self.current_numbering = 0,
                Number::Default => self.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.current_numbering = book_numbering;
                    self.current_chapter[0] = n - 1;
                },
                Number::Hidden => {
                    self.current_numbering = 0;
                    self.current_hide = true;
                },
            }
            content.push_str(&self.render_html(v));
            i+= 1;
        }
        let toc = self.toc.render();

        // If display_toc, display the toc inline
        if self.book.options.get_bool("display_toc").unwrap() {
            content = format!("
{}

{}

{}",
                              self.book.options.get_str("toc_name").unwrap(),
                              &toc,
                              content);
        }

        let template = mustache::compile_str(try!(self.book.get_template("html.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("style",
                        &try!(self.book.get_template("html.css")))
            .insert_str("toc", toc)
            .build();

        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Renders a chapter to HTML
    pub fn render_html(&mut self, tokens: &[Token])-> String {
        let mut res = String::new();
        for token in tokens {
            res.push_str(&self.parse_token(&token));
            self.render_side_notes(&mut res);
        }
        self.render_end_notes(&mut res);
        res
    }

    /// Increase a header
    fn inc_header(&mut self, n: i32) {
        let n = n as usize;
        assert!(n < self.current_chapter.len());
        self.current_chapter[n] += 1;
        for i in n+1..self.current_chapter.len() {
            self.current_chapter[i] = 0;
        }
    }

    /// Returns a "x.y.z"
    fn get_numbers(&self) -> String {
        let mut output = String::new();
        for i in 0..self.current_chapter.len() {
            if self.current_chapter[i] == 0 {
                if i == self.current_chapter.len() - 1 {
                    break;
                }
                let bools:Vec<_> = self.current_chapter[i+1..].iter().map(|x| *x != 0).collect();
                if !bools.contains(&true) {
                    break;
                }
            }
            output.push_str(&format!("{}.", self.current_chapter[i])); //todo
        }
        output
    }

    /// Display side notes if option is to true
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn render_side_notes(&mut self, res: &mut String) {
        if self.book.options.get_bool("side_notes").unwrap() {
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
\n{} {}\n
\n", note_number, footnote));
            }
        }
    }

    /// Display end notes, if side_notes option is set to false
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn render_end_notes(&mut self, res: &mut String) {
        if !self.footnotes.is_empty() {
            res.push_str("
Notes
");
            res.push_str("
");
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
{}	{}
\n", note_number, footnote));
            }
            res.push_str("
");
        }
    }

    /// Transform a vector of `Token`s to HTML format.
    ///
    /// Only public becauses `EpubRenderer` uses it
    #[doc(hidden)]
    pub fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    /// Parse a single token.
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => if self.verbatim {
                escape_html(text)
            } else {
                escape_html(&self.book.clean(text.clone()))
            },
            Token::Paragraph(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Header(n, ref vec) => {
                if self.current_numbering >= n {
                    self.inc_header(n - 1);
                }
                if n == 1 && self.current_hide {
                    return String::new();
                }
                let s = if n == 1 && self.current_numbering >= 1 {
                    let chapter = self.current_chapter[0];
                    self.book.get_header(chapter, &self.render_vec(vec)).unwrap()
                } else if self.current_numbering >= n {
                    format!("{} {}", self.get_numbers(), self.render_vec(vec))
                } else {
                    self.render_vec(vec)
                };
                self.link_number += 1;
                if n <= self.book.options.get_i32("numbering").unwrap() {
                    self.toc.add(n,
                                 format!("{}#link-{}",
                                            self.filename,
                                            self.link_number),
                                 s.clone());
                }
                format!("<h{} id = \"link-{}\">{}</h{}>\n",
                        n, self.link_number, s, n)
            },
            Token::Emphasis(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Strong(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Code(ref vec) => format!("{}", self.render_vec(vec)),
            Token::BlockQuote(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::CodeBlock(ref language, ref vec) => {
                self.verbatim = true;
                let s = self.render_vec(vec);
                let output = if language.is_empty() {
                    format!("
{}
\n", s)
                } else {
                    format!("
{}
\n", language, s)
                };
                self.verbatim = false;
                output
            },
            Token::Rule => String::from("
***
\n"),
            Token::SoftBreak => String::from(" "),
            Token::HardBreak => String::from("
\n"),
            Token::List(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::OrderedList(n, ref vec) => format!("
\n{}
\n",
                                                      if n != 1 {
                                                          format!(" start = \"{}\"", n)
                                                      } else {
                                                          String::new()
                                                      },
                                                      self.render_vec(vec)),
            Token::Item(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Link(ref url, ref title, ref vec) => {
                let url: String = if ResourceHandler::is_local(url) {
                    self.handler.get_link(url).to_owned()
                } else {
                    url.clone()
                };

                format!("{}", url,
                        if title.is_empty() {
                            String::new()
                        } else {
                            format!(" title = \"{}\"", title)
                        },
                        self.render_vec(vec))
            },
            Token::Image(ref url, ref title, ref alt) => {
                let content = self.render_vec(alt);
                let url = self.handler.map_image(Cow::Borrowed(url));
                format!("",
                        url,
                        title,
                        content)
            },
            Token::Table(_, ref vec) => format!("

\n{}

\n", self.render_vec(vec)),
            Token::TableRow(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::TableCell(ref vec) => {
                let tag = if self.table_head {"th"} else {"td"};
                format!("<{}>{}</{}>", tag, self.render_vec(vec), tag)
            },
            Token::TableHead(ref vec) => {
                self.table_head = true;
                let s = self.render_vec(vec);
                self.table_head = false;
                format!("
\n{}
\n", s)
            },
            Token::Footnote(ref vec) => {
                self.footnote_number += 1;
                let number = self.footnote_number;
                assert!(!vec.is_empty());

                let note_number = format!("

  [{}]

", number, number);

                let inner = format!("{}",
                                    if self.epub3 {r#"epub:type="footnote"#}else{""},
                                    number, self.render_vec(vec));
                self.footnotes.push((note_number, inner));

                format!("{}",
                        if self.epub3 {"epub:type = \"noteref\""} else {""},
                        number, number, number)
            },
        }
    }
}

<commit_msg>Add hidden titles to the toc even if the heading is not displayed inline<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use escape::escape_html;
use token::Token;
use book::Book;
use number::Number;
use error::{Error,Result};
use toc::Toc;
use resource_handler::ResourceHandler;
use std::borrow::Cow;

use mustache;

/// Renders HTML document.
///
/// Also used by `EpubRenderer`.
pub struct HtmlRenderer<'a> {
    book: &'a Book,
    table_head: bool,
    footnote_number: u32,
    epub3: bool,
    verbatim: bool,
    link_number: u32,

    // fields used by EpubRenderer so marked public but hidden
    #[doc(hidden)]
    pub toc: Toc,
    #[doc(hidden)]
    pub footnotes: Vec<(String, String)>,
    #[doc(hidden)]
    pub current_chapter: [i32;6],
    #[doc(hidden)]
    pub current_numbering: i32,
    #[doc(hidden)]
    pub current_hide: bool,
    #[doc(hidden)]
    pub filename: String,
    #[doc(hidden)]
    pub handler: ResourceHandler,
}

impl<'a> HtmlRenderer<'a> {
    /// Creates a new HTML renderer
    pub fn new(book: &'a Book) -> HtmlRenderer<'a> {
        HtmlRenderer {
            book: book,
            toc: Toc::new(),
            link_number: 0,
            current_chapter: [0, 0, 0, 0, 0, 0],
            current_numbering: book.options.get_i32("numbering").unwrap(),
            current_hide: false,
            table_head: false,
            footnote_number: 0,
            footnotes: vec!(),
            epub3: false,
            verbatim: false,
            filename: String::new(),
            handler: ResourceHandler::new(),
        }
    }

    /// Render books as a standalone HTML file
    pub fn render_book(&mut self) -> Result<String> {
        for (i, filename) in self.book.filenames.iter().enumerate() {
            self.handler.add_link(filename.clone(), format!("#chapter-{}", i));
        }
        let mut content = String::new();

        let mut i = 0;
        for &(n, ref v) in &self.book.chapters {
            content.push_str(&format!("\n", i));
            self.current_hide = false;
            let book_numbering = self.book.options.get_i32("numbering").unwrap();
            match n {
                Number::Unnumbered => self.current_numbering = 0,
                Number::Default => self.current_numbering = book_numbering,
                Number::Specified(n) => {
                    self.current_numbering = book_numbering;
                    self.current_chapter[0] = n - 1;
                },
                Number::Hidden => {
                    self.current_numbering = 0;
                    self.current_hide = true;
                },
            }
            content.push_str(&self.render_html(v));
            i+= 1;
        }
        let toc = self.toc.render();

        // If display_toc, display the toc inline
        if self.book.options.get_bool("display_toc").unwrap() {
            content = format!("
{}

{}

{}",
                              self.book.options.get_str("toc_name").unwrap(),
                              &toc,
                              content);
        }

        let template = mustache::compile_str(try!(self.book.get_template("html.template")).as_ref());
        let data = self.book.get_mapbuilder("none")
            .insert_str("content", content)
            .insert_str("style",
                        &try!(self.book.get_template("html.css")))
            .insert_str("toc", toc)
            .build();

        let mut res:Vec<u8> = vec!();
        template.render_data(&mut res, &data);
        match String::from_utf8(res) {
            Err(_) => Err(Error::Render("generated HTML was not utf-8 valid")),
            Ok(res) => Ok(res)
        }
    }

    /// Renders a chapter to HTML
    pub fn render_html(&mut self, tokens: &[Token])-> String {
        let mut res = String::new();
        for token in tokens {
            res.push_str(&self.parse_token(&token));
            self.render_side_notes(&mut res);
        }
        self.render_end_notes(&mut res);
        res
    }

    /// Increase a header
    fn inc_header(&mut self, n: i32) {
        let n = n as usize;
        assert!(n < self.current_chapter.len());
        self.current_chapter[n] += 1;
        for i in n+1..self.current_chapter.len() {
            self.current_chapter[i] = 0;
        }
    }

    /// Returns a "x.y.z"
    fn get_numbers(&self) -> String {
        let mut output = String::new();
        for i in 0..self.current_chapter.len() {
            if self.current_chapter[i] == 0 {
                if i == self.current_chapter.len() - 1 {
                    break;
                }
                let bools:Vec<_> = self.current_chapter[i+1..].iter().map(|x| *x != 0).collect();
                if !bools.contains(&true) {
                    break;
                }
            }
            output.push_str(&format!("{}.", self.current_chapter[i])); //todo
        }
        output
    }

    /// Display side notes if option is to true
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn render_side_notes(&mut self, res: &mut String) {
        if self.book.options.get_bool("side_notes").unwrap() {
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
\n{} {}\n
\n", note_number, footnote));
            }
        }
    }

    /// Display end notes, if side_notes option is set to false
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn render_end_notes(&mut self, res: &mut String) {
        if !self.footnotes.is_empty() {
            res.push_str("
Notes
");
            res.push_str("
");
            for (note_number, footnote) in self.footnotes.drain(..) {
                res.push_str(&format!("
{}	{}
\n", note_number, footnote));
            }
            res.push_str("
");
        }
    }

    /// Transform a vector of `Token`s to HTML format.
    ///
    /// Only public becauses `EpubRenderer` uses it
    #[doc(hidden)]
    pub fn render_vec(&mut self, tokens: &[Token]) -> String {
        let mut res = String::new();

        for token in tokens {
            res.push_str(&self.parse_token(&token));
        }
        res
    }

    /// Parse a single token.
    ///
    /// Only public because EpubRenderer uses it
    #[doc(hidden)]
    pub fn parse_token(&mut self, token: &Token) -> String {
        match *token {
            Token::Str(ref text) => if self.verbatim {
                escape_html(text)
            } else {
                escape_html(&self.book.clean(text.clone()))
            },
            Token::Paragraph(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Header(n, ref vec) => {
                if self.current_numbering >= n {
                    self.inc_header(n - 1);
                }
                self.link_number += 1;
                let s = if n == 1 && self.current_numbering >= 1 {
                    let chapter = self.current_chapter[0];
                    self.book.get_header(chapter, &self.render_vec(vec)).unwrap()
                } else if self.current_numbering >= n {
                    format!("{} {}", self.get_numbers(), self.render_vec(vec))
                } else {
                    self.render_vec(vec)
                };
                if n <= self.book.options.get_i32("numbering").unwrap() {
                    self.toc.add(n,
                                 format!("{}#link-{}",
                                            self.filename,
                                            self.link_number),
                                 s.clone());
                }
                if n == 1 && self.current_hide {
                    format!("", self.link_number)
                } else {
                    format!("<h{} id = \"link-{}\">{}</h{}>\n",
                            n, self.link_number, s, n)
                }
            },
            Token::Emphasis(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Strong(ref vec) => format!("{}", self.render_vec(vec)),
            Token::Code(ref vec) => format!("{}", self.render_vec(vec)),
            Token::BlockQuote(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::CodeBlock(ref language, ref vec) => {
                self.verbatim = true;
                let s = self.render_vec(vec);
                let output = if language.is_empty() {
                    format!("
{}
\n", s)
                } else {
                    format!("
{}
\n", language, s)
                };
                self.verbatim = false;
                output
            },
            Token::Rule => String::from("
***
\n"),
            Token::SoftBreak => String::from(" "),
            Token::HardBreak => String::from("
\n"),
            Token::List(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::OrderedList(n, ref vec) => format!("
\n{}
\n",
                                                      if n != 1 {
                                                          format!(" start = \"{}\"", n)
                                                      } else {
                                                          String::new()
                                                      },
                                                      self.render_vec(vec)),
            Token::Item(ref vec) => format!("
{}
\n", self.render_vec(vec)),
            Token::Link(ref url, ref title, ref vec) => {
                let url: String = if ResourceHandler::is_local(url) {
                    self.handler.get_link(url).to_owned()
                } else {
                    url.clone()
                };

                format!("{}", url,
                        if title.is_empty() {
                            String::new()
                        } else {
                            format!(" title = \"{}\"", title)
                        },
                        self.render_vec(vec))
            },
            Token::Image(ref url, ref title, ref alt) => {
                let content = self.render_vec(alt);
                let url = self.handler.map_image(Cow::Borrowed(url));
                format!("",
                        url,
                        title,
                        content)
            },
            Token::Table(_, ref vec) => format!("

\n{}

\n", self.render_vec(vec)),
            Token::TableRow(ref vec) => format!("
\n{}
\n", self.render_vec(vec)),
            Token::TableCell(ref vec) => {
                let tag = if self.table_head {"th"} else {"td"};
                format!("<{}>{}</{}>", tag, self.render_vec(vec), tag)
            },
            Token::TableHead(ref vec) => {
                self.table_head = true;
                let s = self.render_vec(vec);
                self.table_head = false;
                format!("
\n{}
\n", s)
            },
            Token::Footnote(ref vec) => {
                self.footnote_number += 1;
                let number = self.footnote_number;
                assert!(!vec.is_empty());

                let note_number = format!("

  [{}]

", number, number);

                let inner = format!("{}",
                                    if self.epub3 {r#"epub:type="footnote"#}else{""},
                                    number, self.render_vec(vec));
                self.footnotes.push((note_number, inner));

                format!("{}",
                        if self.epub3 {"epub:type = \"noteref\""} else {""},
                        number, number, number)
            },
        }
    }
}

<|endoftext|>


<commit_before>use std::io::Writer;
use std::io::Reader;
use std::vec;

pub struct VecWriter {
    vec: ~[u8],
}

impl VecWriter {
    pub fn new() -> VecWriter {
        VecWriter {
            vec: ~[],
        }
    }
}

impl Writer for VecWriter {
    fn write(&mut self, v: &[u8]) {
        self.vec.push_all(v);
    }

    fn flush(&mut self) {
        fail!();
    }
}

pub struct VecReader {
    vec: ~[u8],
    pos: uint,
}

impl VecReader {
    pub fn new(bytes: ~[u8]) -> VecReader {
        VecReader {
            vec: bytes,
            pos: 0,
        }
    }

    fn remaining(&self) -> uint {
        self.vec.len() - self.pos
    }
}

impl Reader for VecReader {
    fn read(&mut self, bytes: &mut [u8]) -> Option<uint> {
        let n = if bytes.len() < self.remaining() { bytes.len() } else { self.remaining() };
        vec::bytes::copy_memory(bytes, self.vec.slice(self.pos, self.pos + n));
        self.pos += n;
        Some(n)
    }
}

pub struct CountWriter {
    count: uint,
}

impl CountWriter {
    pub fn new() -> CountWriter {
        CountWriter {
            count: 0,
        }
    }
}

impl Writer for CountWriter {
    fn write(&mut self, v: &[u8]) {
        self.count += v.len();
    }
}

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

    use std::io::Writer;

    #[test]
    fn test_vec_writer() {
        let mut w = VecWriter::new();
        fn foo(writer: &mut Writer) {
            writer.write("hi".as_bytes());
        }
        foo(&mut w as &mut Writer);
        assert_eq!(~['h' as u8, 'i' as u8], w.vec.to_owned());
    }

    #[test]
    fn test_count_writer() {
        let mut w = CountWriter::new();
        w.write("hi".as_bytes());
        w.write("there".as_bytes());
        assert_eq!(7, w.count);
    }
}
<commit_msg>Remove unused CountWriter<commit_after>use std::io::Writer;
use std::io::Reader;
use std::vec;

pub struct VecWriter {
    vec: ~[u8],
}

impl VecWriter {
    pub fn new() -> VecWriter {
        VecWriter {
            vec: ~[],
        }
    }
}

impl Writer for VecWriter {
    fn write(&mut self, v: &[u8]) {
        self.vec.push_all(v);
    }

    fn flush(&mut self) {
        fail!();
    }
}

pub struct VecReader {
    vec: ~[u8],
    pos: uint,
}

impl VecReader {
    pub fn new(bytes: ~[u8]) -> VecReader {
        VecReader {
            vec: bytes,
            pos: 0,
        }
    }

    fn remaining(&self) -> uint {
        self.vec.len() - self.pos
    }
}

impl Reader for VecReader {
    fn read(&mut self, bytes: &mut [u8]) -> Option<uint> {
        let n = if bytes.len() < self.remaining() { bytes.len() } else { self.remaining() };
        vec::bytes::copy_memory(bytes, self.vec.slice(self.pos, self.pos + n));
        self.pos += n;
        Some(n)
    }
}

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

    use std::io::Writer;

    #[test]
    fn test_vec_writer() {
        let mut w = VecWriter::new();
        fn foo(writer: &mut Writer) {
            writer.write("hi".as_bytes());
        }
        foo(&mut w as &mut Writer);
        assert_eq!(~['h' as u8, 'i' as u8], w.vec.to_owned());
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::{env, str};
use std::error::Error;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use toml;

use dependency::Dependency;

/// Enumeration of errors which can occur when working with a rust manifest.
quick_error! {
    #[derive(Debug)]
    pub enum ManifestError {
        /// Cargo.toml could not be found.
        MissingManifest {
            description("missing manifest")
            display("Your Cargo.toml is missing.")
        }
        /// The TOML table could not be found.
        NonExistentTable(table: String) {
            description("non existent table")
            display("The table `{}` could not be found.", table)
        }
        /// The dependency could not be found.
        NonExistentDependency(name: String, table: String) {
            description("non existent dependency")
            display("The dependency `{}` could not be found in `{}`.", name, table)
        }
        ParseError(error: String, loline: usize, locol: usize, hiline: usize, hicol: usize) {
            description("parse error")
            display("{}:{}{} {}",
                loline + 1, locol + 1,
                if loline != hiline || locol != hicol {
                    format!("-{}:{}", hiline + 1,
                            hicol + 1)
                } else {
                    "".to_string()
                },
                error)
        }
    }
}

enum CargoFile {
    Config,
    Lock,
}

/// A Cargo Manifest
#[derive(Debug, Clone, PartialEq)]
pub struct Manifest {
    /// Manifest contents as TOML data
    pub data: toml::Table,
}

/// If a manifest is specified, return that one, otherise perform a manifest search starting from
/// the current directory.
/// If a manifest is specified, return that one. If a path is specified, perform a manifest search
/// starting from there. If nothing is specified, start searching from the current directory
/// (`cwd`).
fn find(specified: &Option<&str>, file: CargoFile) -> Result<PathBuf, Box<Error>> {
    let file_path = specified.map(PathBuf::from);

    if let Some(path) = file_path {
        if try!(fs::metadata(&path)).is_file() {
            Ok(path)
        } else {
            search(&path, file).map_err(From::from)
        }
    } else {
        env::current_dir()
            .map_err(From::from)
            .and_then(|ref dir| search(dir, file).map_err(From::from))
    }
}

/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
fn search(dir: &Path, file: CargoFile) -> Result<PathBuf, ManifestError> {
    let manifest = match file {
        CargoFile::Config => dir.join("Cargo.toml"),
        CargoFile::Lock => dir.join("Cargo.lock"),
    };

    fs::metadata(&manifest)
        .map(|_| manifest)
        .or(dir.parent().ok_or(ManifestError::MissingManifest).and_then(|dir| search(dir, file)))
}

impl Manifest {
    /// Look for a `Cargo.toml` file
    ///
    /// Starts at the given path an goes into its parent directories until the manifest file is
    /// found. If no path is given, the process's working directory is used as a starting point.
    pub fn find_file(path: &Option<&str>) -> Result<File, Box<Error>> {
        find(path, CargoFile::Config).and_then(|path| {
            OpenOptions::new()
                .read(true)
                .write(true)
                .open(path)
                .map_err(From::from)
        })
    }

    /// Look for a `Cargo.lock` file
    ///
    /// Starts at the given path an goes into its parent directories until the manifest file is
    /// found. If no path is given, the process' working directory is used as a starting point.
    pub fn find_lock_file(path: &Option<&str>) -> Result<File, Box<Error>> {
        find(path, CargoFile::Lock).and_then(|path| {
            OpenOptions::new()
                .read(true)
                .write(true)
                .open(path)
                .map_err(From::from)
        })
    }

    /// Open the `Cargo.toml` for a path (or the process' `cwd`)
    pub fn open(path: &Option<&str>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        data.parse()
    }

    /// Open the `Cargo.lock` for a path (or the process' `cwd`)
    pub fn open_lock_file(path: &Option<&str>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_lock_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        data.parse()
    }

    /// Overwrite a file with TOML data.
    pub fn write_to_file(&self, file: &mut File) -> Result<(), Box<Error>> {
        let mut toml = self.data.clone();

        let (proj_header, proj_data) = try!(toml.remove("package")
                                                .map(|data| ("package", data))
                                                .or_else(|| {
                                                    toml.remove("project")
                                                        .map(|data| ("project", data))
                                                })
                                                .ok_or(ManifestError::MissingManifest));

        let new_contents = format!("[{}]\n{}{}",
                                   proj_header,
                                   proj_data,
                                   toml::Value::Table(toml));
        let new_contents_bytes = new_contents.as_bytes();

        // We need to truncate the file, otherwise the new contents
        // will be mixed up with the old ones.
        try!(file.set_len(new_contents_bytes.len() as u64));
        file.write_all(new_contents_bytes).map_err(From::from)
    }

    /// Add entry to a Cargo.toml.
    #[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
    pub fn insert_into_table(&mut self,
                             table: &str,
                             dep: &Dependency)
                             -> Result<(), ManifestError> {
        let (ref name, ref data) = dep.to_toml();
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table))
                            .or_insert_with(|| toml::Value::Table(BTreeMap::new()));
        match *entry {
            toml::Value::Table(ref mut deps) => {
                deps.insert(name.clone(), data.clone());
                Ok(())
            }
            _ => Err(ManifestError::NonExistentTable(table.into())),
        }
    }

    /// Remove entry from a Cargo.toml.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate cargo_edit;
    /// # extern crate toml;
    /// # fn main() {
    ///     use cargo_edit::{Dependency, Manifest};
    ///     use toml;
    ///
    ///     let mut manifest = Manifest { data: toml::Table::new() };
    ///     let dep = Dependency::new("cargo-edit").set_version("0.1.0");
    ///     let _ = manifest.insert_into_table("dependencies", &dep);
    ///     assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
    ///     assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    ///     assert!(manifest.data.is_empty());
    /// # }
    /// ```
    #[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
    pub fn remove_from_table(&mut self, table: &str, name: &str) -> Result<(), ManifestError> {
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table));

        match entry {
            Entry::Vacant(_) => Err(ManifestError::NonExistentTable(table.into())),
            Entry::Occupied(mut section) => {
                let result = match *section.get_mut() {
                    toml::Value::Table(ref mut deps) => {
                        deps.remove(name)
                            .map(|_| ())
                            .ok_or_else(|| ManifestError::NonExistentDependency(name.into(), table.into()))
                    }
                    _ => Err(ManifestError::NonExistentTable(table.into())),
                };
                if let Some(empty) = section.get().as_table().and_then(|x| Some(x.is_empty())) {
                    if empty {
                        section.remove();
                    }
                }
                result
            }
        }
    }

    /// Add multiple dependencies to manifest
    pub fn add_deps(&mut self, table: &str, deps: &[Dependency]) -> Result<(), Box<Error>> {
        deps.iter()
            .map(|dep| self.insert_into_table(table, &dep))
            .collect::<Result<Vec<_>, _>>()
            .map_err(From::from)
            .map(|_| ())
    }
}

impl str::FromStr for Manifest {
    type Err = Box<Error>;

    /// Read manifest data from string
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let mut parser = toml::Parser::new(&input);

        parser.parse()
              .ok_or(format_parse_error(parser))
              .map_err(Option::unwrap)
              .map_err(From::from)
              .map(|data| Manifest { data: data })
    }
}

fn format_parse_error(mut parser: toml::Parser) -> Option<ManifestError> {
    parser.errors.pop().map(|error| {
        let (loline, locol) = parser.to_linecol(error.lo);
        let (hiline, hicol) = parser.to_linecol(error.hi);
        ManifestError::ParseError(error.desc.clone(), loline, locol, hiline, hicol)
    })
}

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

    #[test]
    fn add_remove_dependency() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let clone = manifest.clone();
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        let _ = manifest.insert_into_table("dependencies", &dep);
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
        assert_eq!(manifest, clone);
    }

    #[test]
    fn remove_dependency_no_section() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    }

    #[test]
    fn remove_dependency_non_existent() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        let other_dep = Dependency::new("other-dep").set_version("0.1.0");
        let _ = manifest.insert_into_table("dependencies", &other_dep);
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    }
}
<commit_msg>Use named placeholders when formatting TOML parse error<commit_after>use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::{env, str};
use std::error::Error;
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use toml;

use dependency::Dependency;

/// Enumeration of errors which can occur when working with a rust manifest.
quick_error! {
    #[derive(Debug)]
    pub enum ManifestError {
        /// Cargo.toml could not be found.
        MissingManifest {
            description("missing manifest")
            display("Your Cargo.toml is missing.")
        }
        /// The TOML table could not be found.
        NonExistentTable(table: String) {
            description("non existent table")
            display("The table `{}` could not be found.", table)
        }
        /// The dependency could not be found.
        NonExistentDependency(name: String, table: String) {
            description("non existent dependency")
            display("The dependency `{}` could not be found in `{}`.", name, table)
        }
        ParseError(error: String, loline: usize, locol: usize, hiline: usize, hicol: usize) {
            description("parse error")
            display("{line}:{col}{upto} {error_msg}",
                line = loline + 1,
                col = locol + 1,
                upto = if loline != hiline || locol != hicol {
                    format!("-{}:{}", hiline + 1, hicol + 1)
                } else {
                    "".to_string()
                },
                error_msg = error)
        }
    }
}

enum CargoFile {
    Config,
    Lock,
}

/// A Cargo Manifest
#[derive(Debug, Clone, PartialEq)]
pub struct Manifest {
    /// Manifest contents as TOML data
    pub data: toml::Table,
}

/// If a manifest is specified, return that one, otherise perform a manifest search starting from
/// the current directory.
/// If a manifest is specified, return that one. If a path is specified, perform a manifest search
/// starting from there. If nothing is specified, start searching from the current directory
/// (`cwd`).
fn find(specified: &Option<&str>, file: CargoFile) -> Result<PathBuf, Box<Error>> {
    let file_path = specified.map(PathBuf::from);

    if let Some(path) = file_path {
        if try!(fs::metadata(&path)).is_file() {
            Ok(path)
        } else {
            search(&path, file).map_err(From::from)
        }
    } else {
        env::current_dir()
            .map_err(From::from)
            .and_then(|ref dir| search(dir, file).map_err(From::from))
    }
}

/// Search for Cargo.toml in this directory and recursively up the tree until one is found.
fn search(dir: &Path, file: CargoFile) -> Result<PathBuf, ManifestError> {
    let manifest = match file {
        CargoFile::Config => dir.join("Cargo.toml"),
        CargoFile::Lock => dir.join("Cargo.lock"),
    };

    fs::metadata(&manifest)
        .map(|_| manifest)
        .or(dir.parent().ok_or(ManifestError::MissingManifest).and_then(|dir| search(dir, file)))
}

impl Manifest {
    /// Look for a `Cargo.toml` file
    ///
    /// Starts at the given path an goes into its parent directories until the manifest file is
    /// found. If no path is given, the process's working directory is used as a starting point.
    pub fn find_file(path: &Option<&str>) -> Result<File, Box<Error>> {
        find(path, CargoFile::Config).and_then(|path| {
            OpenOptions::new()
                .read(true)
                .write(true)
                .open(path)
                .map_err(From::from)
        })
    }

    /// Look for a `Cargo.lock` file
    ///
    /// Starts at the given path an goes into its parent directories until the manifest file is
    /// found. If no path is given, the process' working directory is used as a starting point.
    pub fn find_lock_file(path: &Option<&str>) -> Result<File, Box<Error>> {
        find(path, CargoFile::Lock).and_then(|path| {
            OpenOptions::new()
                .read(true)
                .write(true)
                .open(path)
                .map_err(From::from)
        })
    }

    /// Open the `Cargo.toml` for a path (or the process' `cwd`)
    pub fn open(path: &Option<&str>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        data.parse()
    }

    /// Open the `Cargo.lock` for a path (or the process' `cwd`)
    pub fn open_lock_file(path: &Option<&str>) -> Result<Manifest, Box<Error>> {
        let mut file = try!(Manifest::find_lock_file(path));
        let mut data = String::new();
        try!(file.read_to_string(&mut data));

        data.parse()
    }

    /// Overwrite a file with TOML data.
    pub fn write_to_file(&self, file: &mut File) -> Result<(), Box<Error>> {
        let mut toml = self.data.clone();

        let (proj_header, proj_data) = try!(toml.remove("package")
                                                .map(|data| ("package", data))
                                                .or_else(|| {
                                                    toml.remove("project")
                                                        .map(|data| ("project", data))
                                                })
                                                .ok_or(ManifestError::MissingManifest));

        let new_contents = format!("[{}]\n{}{}",
                                   proj_header,
                                   proj_data,
                                   toml::Value::Table(toml));
        let new_contents_bytes = new_contents.as_bytes();

        // We need to truncate the file, otherwise the new contents
        // will be mixed up with the old ones.
        try!(file.set_len(new_contents_bytes.len() as u64));
        file.write_all(new_contents_bytes).map_err(From::from)
    }

    /// Add entry to a Cargo.toml.
    #[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
    pub fn insert_into_table(&mut self,
                             table: &str,
                             dep: &Dependency)
                             -> Result<(), ManifestError> {
        let (ref name, ref data) = dep.to_toml();
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table))
                            .or_insert_with(|| toml::Value::Table(BTreeMap::new()));
        match *entry {
            toml::Value::Table(ref mut deps) => {
                deps.insert(name.clone(), data.clone());
                Ok(())
            }
            _ => Err(ManifestError::NonExistentTable(table.into())),
        }
    }

    /// Remove entry from a Cargo.toml.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate cargo_edit;
    /// # extern crate toml;
    /// # fn main() {
    ///     use cargo_edit::{Dependency, Manifest};
    ///     use toml;
    ///
    ///     let mut manifest = Manifest { data: toml::Table::new() };
    ///     let dep = Dependency::new("cargo-edit").set_version("0.1.0");
    ///     let _ = manifest.insert_into_table("dependencies", &dep);
    ///     assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
    ///     assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    ///     assert!(manifest.data.is_empty());
    /// # }
    /// ```
    #[cfg_attr(feature = "dev", allow(toplevel_ref_arg))]
    pub fn remove_from_table(&mut self, table: &str, name: &str) -> Result<(), ManifestError> {
        let ref mut manifest = self.data;
        let entry = manifest.entry(String::from(table));

        match entry {
            Entry::Vacant(_) => Err(ManifestError::NonExistentTable(table.into())),
            Entry::Occupied(mut section) => {
                let result = match *section.get_mut() {
                    toml::Value::Table(ref mut deps) => {
                        deps.remove(name)
                            .map(|_| ())
                            .ok_or_else(|| ManifestError::NonExistentDependency(name.into(), table.into()))
                    }
                    _ => Err(ManifestError::NonExistentTable(table.into())),
                };
                if let Some(empty) = section.get().as_table().and_then(|x| Some(x.is_empty())) {
                    if empty {
                        section.remove();
                    }
                }
                result
            }
        }
    }

    /// Add multiple dependencies to manifest
    pub fn add_deps(&mut self, table: &str, deps: &[Dependency]) -> Result<(), Box<Error>> {
        deps.iter()
            .map(|dep| self.insert_into_table(table, &dep))
            .collect::<Result<Vec<_>, _>>()
            .map_err(From::from)
            .map(|_| ())
    }
}

impl str::FromStr for Manifest {
    type Err = Box<Error>;

    /// Read manifest data from string
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let mut parser = toml::Parser::new(&input);

        parser.parse()
              .ok_or(format_parse_error(parser))
              .map_err(Option::unwrap)
              .map_err(From::from)
              .map(|data| Manifest { data: data })
    }
}

fn format_parse_error(mut parser: toml::Parser) -> Option<ManifestError> {
    parser.errors.pop().map(|error| {
        let (loline, locol) = parser.to_linecol(error.lo);
        let (hiline, hicol) = parser.to_linecol(error.hi);
        ManifestError::ParseError(error.desc.clone(), loline, locol, hiline, hicol)
    })
}

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

    #[test]
    fn add_remove_dependency() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let clone = manifest.clone();
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        let _ = manifest.insert_into_table("dependencies", &dep);
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_ok());
        assert_eq!(manifest, clone);
    }

    #[test]
    fn remove_dependency_no_section() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    }

    #[test]
    fn remove_dependency_non_existent() {
        let mut manifest = Manifest { data: toml::Table::new() };
        let dep = Dependency::new("cargo-edit").set_version("0.1.0");
        let other_dep = Dependency::new("other-dep").set_version("0.1.0");
        let _ = manifest.insert_into_table("dependencies", &other_dep);
        assert!(manifest.remove_from_table("dependencies", &dep.name).is_err());
    }
}
<|endoftext|>