<commit_before>use crate::options::*;

use nom::bytes::complete::{tag, take};
use nom::multi::{many0, many_till};
use nom::number::complete::{be_u16, be_u32, be_u8};
use std::net::Ipv4Addr;

pub enum Err<I> {
    NomError(nom::Err<(I, nom::error::ErrorKind)>),
    NonUtf8String,
    UnrecognizedMessageType,
    InvalidHlen,
}

impl<I> nom::error::ParseError<I> for Err<I> {
    fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self {
        Err::NomError(nom::Err::Error((input, kind)))
    }

    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
        other
    }
}

type IResult<I, O> = nom::IResult<I, O, Err<I>>;

/// DHCP Packet Structure
pub struct Packet {
    pub reply: bool, // false = request, true = reply
    pub hops: u8,
    pub xid: u32, // Random identifier
    pub secs: u16,
    pub broadcast: bool,
    pub ciaddr: Ipv4Addr,
    pub yiaddr: Ipv4Addr,
    pub siaddr: Ipv4Addr,
    pub giaddr: Ipv4Addr,
    pub chaddr: [u8; 6],
    pub options: Vec<DhcpOption>,
}

fn decode_reply(input: &[u8]) -> IResult<&[u8], bool> {
    let (input, reply) = take(1u8)(input)?;
    Ok((
        input,
        match reply[0] {
            BOOT_REPLY => true,
            BOOT_REQUEST => false,
            _ => {
                // @TODO: Throw an error
                false
            }
        },
    ))
}

fn decode_ipv4(p: &[u8]) -> IResult<&[u8], Ipv4Addr> {
    let (input, addr) = take(4u8)(p)?;
    Ok((input, Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3])))
}

pub fn decode_option(input: &[u8]) -> IResult<&[u8], DhcpOption> {
    let (input, code) = be_u8(input)?;
    assert!(code != END);

    let (input, len) = be_u8(input)?;
    let (input, data) = take(len)(input)?;
    let option = match code {
        DHCP_MESSAGE_TYPE => DhcpOption::DhcpMessageType(match MessageType::from(be_u8(data)?.1) {
            Ok(x) => x,
            Err(_) => return Err(nom::Err::Error(Err::UnrecognizedMessageType)),
        }),
        SERVER_IDENTIFIER => DhcpOption::ServerIdentifier(decode_ipv4(data)?.1),
        PARAMETER_REQUEST_LIST => DhcpOption::ParameterRequestList(data.to_vec()),
        REQUESTED_IP_ADDRESS => DhcpOption::RequestedIpAddress(decode_ipv4(data)?.1),
        HOST_NAME => DhcpOption::HostName(match std::str::from_utf8(data) {
            Ok(s) => s.to_string(),
            Err(_) => return Err(nom::Err::Error(Err::NonUtf8String)),
        }),
        ROUTER => DhcpOption::Router(many0(decode_ipv4)(data)?.1),
        DOMAIN_NAME_SERVER => DhcpOption::DomainNameServer(many0(decode_ipv4)(data)?.1),
        IP_ADDRESS_LEASE_TIME => DhcpOption::IpAddressLeaseTime(be_u32(data)?.1),
        SUBNET_MASK => DhcpOption::SubnetMask(decode_ipv4(data)?.1),
        MESSAGE => DhcpOption::Message(match std::str::from_utf8(data) {
            Ok(s) => s.to_string(),
            Err(_) => return Err(nom::Err::Error(Err::NonUtf8String)),
        }),
        _ => DhcpOption::Unrecognized(RawDhcpOption {
            code,
            data: data.to_vec(),
        }),
    };
    Ok((input, option))
}

/// Parses Packet from byte array
fn decode(input: &[u8]) -> IResult<&[u8], Packet> {
    let (options_input, input) = take(236u32)(input)?;

    let (input, reply) = decode_reply(input)?;
    let (input, _htype) = take(1u8)(input)?;
    let (input, hlen) = be_u8(input)?;
    let (input, hops) = be_u8(input)?;
    let (input, xid) = be_u32(input)?;
    let (input, secs) = be_u16(input)?;
    let (input, flags) = be_u16(input)?;
    let (input, ciaddr) = decode_ipv4(input)?;
    let (input, yiaddr) = decode_ipv4(input)?;
    let (input, siaddr) = decode_ipv4(input)?;
    let (input, giaddr) = decode_ipv4(input)?;

    if hlen != 6 {
        return Err(nom::Err::Error(Err::InvalidHlen));
    }
    let (_, chaddr) = take(6u8)(input)?;

    let input = options_input;
    let (input, _) = tag(COOKIE)(input)?;

    let (input, (options, _)) = many_till(decode_option, tag(&[END]))(input)?;

    Ok((
        input,
        Packet {
            reply,
            hops,
            secs,
            broadcast: flags & 128 == 128,
            ciaddr,
            yiaddr,
            siaddr,
            giaddr,
            options,
            chaddr: [
                chaddr[0], chaddr[1], chaddr[2], chaddr[3], chaddr[4], chaddr[5],
            ],
            xid,
        },
    ))
}

impl Packet {
    pub fn from(input: &[u8]) -> Result<Packet, nom::Err<Err<&[u8]>>> {
        Ok(decode(input)?.1)
    }

    /// Extracts requested option payload from packet if available
    pub fn option(&self, code: u8) -> Option<&DhcpOption> {
        for option in &self.options {
            if option.code() == code {
                return Some(&option);
            }
        }
        None
    }

    /// Convenience function for extracting a packet's message type.
    pub fn message_type(&self) -> Result<MessageType, String> {
        match self.option(DHCP_MESSAGE_TYPE) {
            Some(DhcpOption::DhcpMessageType(msgtype)) => Ok(*msgtype),
            Some(option) => Err(format![
                "Got wrong enum code {} for DHCP_MESSAGE_TYPE",
                option.code()
            ]),
            None => Err("Packet does not have MessageType option".to_string()),
        }
    }

    /// Creates byte array DHCP packet
    pub fn encode<'c>(&'c self, p: &'c mut [u8]) -> &[u8] {
        p[..12].clone_from_slice(&[
            (if self.reply { BOOT_REPLY } else { BOOT_REQUEST }),
            1,
            6,
            self.hops,
            ((self.xid >> 24) & 0xFF) as u8,
            ((self.xid >> 16) & 0xFF) as u8,
            ((self.xid >> 8) & 0xFF) as u8,
            (self.xid & 0xFF) as u8,
            (self.secs >> 8) as u8,
            (self.secs & 255) as u8,
            (if self.broadcast { 128 } else { 0 }),
            0,
        ]);
        p[12..16].clone_from_slice(&self.ciaddr.octets());
        p[16..20].clone_from_slice(&self.yiaddr.octets());
        p[20..24].clone_from_slice(&self.siaddr.octets());
        p[24..28].clone_from_slice(&self.giaddr.octets());
        p[28..34].clone_from_slice(&self.chaddr);
        p[34..236].clone_from_slice(&[0; 202]);
        p[236..240].clone_from_slice(&COOKIE);

        let mut length: usize = 240;
        for option in &self.options {
            let option = option.to_raw();
            p[length] = option.code;
            p[length + 1] = option.data.len() as u8;
            p[length + 2..length + 2 + option.data.len()].clone_from_slice(&option.data);
            length += 2 + option.data.len();
        }
        p[length] = END;
        length += 1;
        if length < 272 {
            // Pad to min size
            p[length..272].clone_from_slice(&[PAD; 32][..272 - length]);
            length = 272
        }
        &p[..length]
    }
}

const COOKIE: [u8; 4] = [99, 130, 83, 99];

const BOOT_REQUEST: u8 = 1; // From Client;
const BOOT_REPLY: u8 = 2; // From Server;

const END: u8 = 255;
const PAD: u8 = 0;
<commit_msg>add packet debug<commit_after>use crate::options::*;

use nom::bytes::complete::{tag, take};
use nom::multi::{many0, many_till};
use nom::number::complete::{be_u16, be_u32, be_u8};
use std::net::Ipv4Addr;

pub enum Err<I> {
    NomError(nom::Err<(I, nom::error::ErrorKind)>),
    NonUtf8String,
    UnrecognizedMessageType,
    InvalidHlen,
}

impl<I> nom::error::ParseError<I> for Err<I> {
    fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self {
        Err::NomError(nom::Err::Error((input, kind)))
    }

    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
        other
    }
}

type IResult<I, O> = nom::IResult<I, O, Err<I>>;

/// DHCP Packet Structure
#[derive(Debug)]
pub struct Packet {
    pub reply: bool, // false = request, true = reply
    pub hops: u8,
    pub xid: u32, // Random identifier
    pub secs: u16,
    pub broadcast: bool,
    pub ciaddr: Ipv4Addr,
    pub yiaddr: Ipv4Addr,
    pub siaddr: Ipv4Addr,
    pub giaddr: Ipv4Addr,
    pub chaddr: [u8; 6],
    pub options: Vec<DhcpOption>,
}

fn decode_reply(input: &[u8]) -> IResult<&[u8], bool> {
    let (input, reply) = take(1u8)(input)?;
    Ok((
        input,
        match reply[0] {
            BOOT_REPLY => true,
            BOOT_REQUEST => false,
            _ => {
                // @TODO: Throw an error
                false
            }
        },
    ))
}

fn decode_ipv4(p: &[u8]) -> IResult<&[u8], Ipv4Addr> {
    let (input, addr) = take(4u8)(p)?;
    Ok((input, Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3])))
}

pub fn decode_option(input: &[u8]) -> IResult<&[u8], DhcpOption> {
    let (input, code) = be_u8(input)?;
    assert!(code != END);

    let (input, len) = be_u8(input)?;
    let (input, data) = take(len)(input)?;
    let option = match code {
        DHCP_MESSAGE_TYPE => DhcpOption::DhcpMessageType(match MessageType::from(be_u8(data)?.1) {
            Ok(x) => x,
            Err(_) => return Err(nom::Err::Error(Err::UnrecognizedMessageType)),
        }),
        SERVER_IDENTIFIER => DhcpOption::ServerIdentifier(decode_ipv4(data)?.1),
        PARAMETER_REQUEST_LIST => DhcpOption::ParameterRequestList(data.to_vec()),
        REQUESTED_IP_ADDRESS => DhcpOption::RequestedIpAddress(decode_ipv4(data)?.1),
        HOST_NAME => DhcpOption::HostName(match std::str::from_utf8(data) {
            Ok(s) => s.to_string(),
            Err(_) => return Err(nom::Err::Error(Err::NonUtf8String)),
        }),
        ROUTER => DhcpOption::Router(many0(decode_ipv4)(data)?.1),
        DOMAIN_NAME_SERVER => DhcpOption::DomainNameServer(many0(decode_ipv4)(data)?.1),
        IP_ADDRESS_LEASE_TIME => DhcpOption::IpAddressLeaseTime(be_u32(data)?.1),
        SUBNET_MASK => DhcpOption::SubnetMask(decode_ipv4(data)?.1),
        MESSAGE => DhcpOption::Message(match std::str::from_utf8(data) {
            Ok(s) => s.to_string(),
            Err(_) => return Err(nom::Err::Error(Err::NonUtf8String)),
        }),
        _ => DhcpOption::Unrecognized(RawDhcpOption {
            code,
            data: data.to_vec(),
        }),
    };
    Ok((input, option))
}

/// Parses Packet from byte array
fn decode(input: &[u8]) -> IResult<&[u8], Packet> {
    let (options_input, input) = take(236u32)(input)?;

    let (input, reply) = decode_reply(input)?;
    let (input, _htype) = take(1u8)(input)?;
    let (input, hlen) = be_u8(input)?;
    let (input, hops) = be_u8(input)?;
    let (input, xid) = be_u32(input)?;
    let (input, secs) = be_u16(input)?;
    let (input, flags) = be_u16(input)?;
    let (input, ciaddr) = decode_ipv4(input)?;
    let (input, yiaddr) = decode_ipv4(input)?;
    let (input, siaddr) = decode_ipv4(input)?;
    let (input, giaddr) = decode_ipv4(input)?;

    if hlen != 6 {
        return Err(nom::Err::Error(Err::InvalidHlen));
    }
    let (_, chaddr) = take(6u8)(input)?;

    let input = options_input;
    let (input, _) = tag(COOKIE)(input)?;

    let (input, (options, _)) = many_till(decode_option, tag(&[END]))(input)?;

    Ok((
        input,
        Packet {
            reply,
            hops,
            secs,
            broadcast: flags & 128 == 128,
            ciaddr,
            yiaddr,
            siaddr,
            giaddr,
            options,
            chaddr: [
                chaddr[0], chaddr[1], chaddr[2], chaddr[3], chaddr[4], chaddr[5],
            ],
            xid,
        },
    ))
}

impl Packet {
    pub fn from(input: &[u8]) -> Result<Packet, nom::Err<Err<&[u8]>>> {
        Ok(decode(input)?.1)
    }

    /// Extracts requested option payload from packet if available
    pub fn option(&self, code: u8) -> Option<&DhcpOption> {
        for option in &self.options {
            if option.code() == code {
                return Some(&option);
            }
        }
        None
    }

    /// Convenience function for extracting a packet's message type.
    pub fn message_type(&self) -> Result<MessageType, String> {
        match self.option(DHCP_MESSAGE_TYPE) {
            Some(DhcpOption::DhcpMessageType(msgtype)) => Ok(*msgtype),
            Some(option) => Err(format![
                "Got wrong enum code {} for DHCP_MESSAGE_TYPE",
                option.code()
            ]),
            None => Err("Packet does not have MessageType option".to_string()),
        }
    }

    /// Creates byte array DHCP packet
    pub fn encode<'c>(&'c self, p: &'c mut [u8]) -> &[u8] {
        p[..12].clone_from_slice(&[
            (if self.reply { BOOT_REPLY } else { BOOT_REQUEST }),
            1,
            6,
            self.hops,
            ((self.xid >> 24) & 0xFF) as u8,
            ((self.xid >> 16) & 0xFF) as u8,
            ((self.xid >> 8) & 0xFF) as u8,
            (self.xid & 0xFF) as u8,
            (self.secs >> 8) as u8,
            (self.secs & 255) as u8,
            (if self.broadcast { 128 } else { 0 }),
            0,
        ]);
        p[12..16].clone_from_slice(&self.ciaddr.octets());
        p[16..20].clone_from_slice(&self.yiaddr.octets());
        p[20..24].clone_from_slice(&self.siaddr.octets());
        p[24..28].clone_from_slice(&self.giaddr.octets());
        p[28..34].clone_from_slice(&self.chaddr);
        p[34..236].clone_from_slice(&[0; 202]);
        p[236..240].clone_from_slice(&COOKIE);

        let mut length: usize = 240;
        for option in &self.options {
            let option = option.to_raw();
            p[length] = option.code;
            p[length + 1] = option.data.len() as u8;
            p[length + 2..length + 2 + option.data.len()].clone_from_slice(&option.data);
            length += 2 + option.data.len();
        }
        p[length] = END;
        length += 1;
        if length < 272 {
            // Pad to min size
            p[length..272].clone_from_slice(&[PAD; 32][..272 - length]);
            length = 272
        }
        &p[..length]
    }
}

const COOKIE: [u8; 4] = [99, 130, 83, 99];

const BOOT_REQUEST: u8 = 1; // From Client;
const BOOT_REPLY: u8 = 2; // From Server;

const END: u8 = 255;
const PAD: u8 = 0;
<|endoftext|>


<commit_before>use futures::{Async, AsyncSink, Future, Poll};
use futures::stream::Stream;
use futures::sink::Sink;
use state_machine_future::RentToOwn;
use tokio_core::reactor::Handle;
use tokio_retry::{self, Retry};
use tokio_retry::strategy::{FibonacciBackoff, jitter};
use std::io;
use std::error;
use std::time::{Duration, Instant};
use std::fmt;
use std::collections::VecDeque;

pub type ConnectionError = io::Error;
pub type SessionId = u32;

#[derive(Debug, Clone, PartialEq)]
pub enum Event {
    Connected {
        session_id: SessionId,
    }
}

pub enum Message<Item> {
    Packet {
        session_id: SessionId,
        content: Item,
    },
    Control {
        event: Event,
    }
}

pub struct TimestampedItem<Item> {
    pub timestamp: Instant,
    pub item: Item,
}

impl <Item> TimestampedItem<Item> {
    pub fn new(item: Item) -> TimestampedItem<Item> {
        TimestampedItem {
            timestamp: Instant::now(),
            item: item,
        }
    }

    pub fn age(&self) -> Duration {
        Instant::now().duration_since(self.timestamp)
    }

    pub fn older_than(&self, age: Duration) -> bool {
        self.age().gt(&age)
    }
}

impl <Item> Clone for Message<Item> where Item: Clone {
    fn clone(&self) -> Message<Item> {
        match self {
            &Message::Packet { session_id, ref content } => {
                Message::Packet {
                    session_id: session_id,
                    content: content.clone()
                }
            },
            &Message::Control { ref event } => {
                Message::Control {
                    event: event.clone()
                }
            }
        }
    }
}

impl <Item> fmt::Debug for Message<Item> where Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Packet { session_id, ref content } => {
                write!(f, "Message::Packet<session_id={:?}, content={:?}>", session_id, content)
            },
            &Message::Control { ref event } => {
                write!(f, "Message::Control<event={:?}>", event)
            }
        }
    }
}

pub trait NewTransport {
    type Future: Future<Item=Self::Transport,Error=Self::Error>;
    type Transport;
    type Error: error::Error + Sync + Send;

    fn new_transport(&self) -> Self::Future;
}

#[derive(StateMachineFuture)]
pub enum Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    #[state_machine_future(start, transitions(Connecting, Finished, Error))]
    NotConnected {
        handle: Handle,
        stream: S,
        sink: K,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(NotConnected, Connected, Finished, Error))]
    Connecting {
        handle: Handle,
        stream: S,
        sink: K,
        tcp_future: Box<Future<Item=T,Error=io::Error>>,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(Connected, NotConnected, Finished, Error))]
    Connected {
        handle: Handle,
        stream: S,
        sink: K,
        tcp: T,
        outbound: VecDeque<TimestampedItem<Item>>,
        outbound_max: usize,
        outbound_max_age: Duration,
        outbound_inflight: bool,
        inbound: VecDeque<Message<Item>>,
        inbound_max: usize,
        inbound_inflight: bool,
        error_count: u32,
        session_id: u32,
        new_transport: N,
    },

    #[state_machine_future(ready)]
    Finished(()),

    #[state_machine_future(error)]
    Error(ConnectionError),
}

impl <Item, S, K, T, N> Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    pub fn new(handle: Handle, stream: S, sink: K, new_transport: N, inbound_max: usize, outbound_max: usize, outbound_max_age: Duration) -> ConnectionFuture<Item, S, K, T, N> {
        debug!("constructing new connection future");
        Connection::start(handle, stream, sink, 0, 0, new_transport, inbound_max, outbound_max, outbound_max_age)
    }
}

impl <Item, S, K, T, N> PollConnection<Item, S, K, T, N> for Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    fn poll_not_connected<'a>(not_connected: &'a mut RentToOwn<'a, NotConnected<Item, S, K, T, N>>) -> Poll<AfterNotConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("not connected");

        let retry_strategy = FibonacciBackoff ::from_millis(1)
            .max_delay(Duration::from_millis(2000))
            .map(jitter);

        let handle = not_connected.handle.clone();
        let new_transport = not_connected.new_transport.clone();

        let tcp_future = Box::new(Retry::spawn(handle, retry_strategy, move || {
            new_transport.new_transport()
        }).map_err(|e| {
            match e {
                tokio_retry::Error::OperationError(e) => io::Error::new(io::ErrorKind::Other, e),
                tokio_retry::Error::TimerError(e) => e,
            }
        })) as (Box<Future<Item=T,Error=io::Error>>);

        let not_connected = not_connected.take();
        Ok(Async::Ready(Connecting {
            handle: not_connected.handle,
            stream: not_connected.stream,
            sink: not_connected.sink,
            tcp_future: tcp_future,
            error_count: not_connected.error_count,
            session_id: not_connected.session_id,
            new_transport: not_connected.new_transport,
            inbound_max: not_connected.inbound_max,
            outbound_max: not_connected.outbound_max,
            outbound_max_age: not_connected.outbound_max_age,
        }.into()))
    }

    fn poll_connecting<'a>(connecting: &'a mut RentToOwn<'a, Connecting<Item, S, K, T, N>>) -> Poll<AfterConnecting<Item, S, K, T, N>, ConnectionError> {
        trace!("connecting");

        let tcp = try_ready!(connecting.tcp_future.poll());

        let connecting = connecting.take();
        Ok(Async::Ready(Connected {
            handle: connecting.handle,
            stream: connecting.stream,
            sink: connecting.sink,
            tcp: tcp,
            inbound: VecDeque::from(vec![
                Message::Control {
                    event: Event::Connected {
                        session_id: connecting.session_id
                    }
                }
            ]),
            inbound_max: connecting.inbound_max,
            inbound_inflight: false,
            outbound: VecDeque::new(),
            outbound_inflight: false,
            outbound_max: connecting.outbound_max,
            outbound_max_age: connecting.outbound_max_age,
            error_count: connecting.error_count,
            session_id: connecting.session_id,
            new_transport: connecting.new_transport,
        }.into()))
    }

    fn poll_connected<'a>(connected: &'a mut RentToOwn<'a, Connected<Item, S, K, T, N>>) -> Poll<AfterConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("connected");

        // we make progress if any poll returns Ready
        // if we make progress then we return a state or state change
        // otherwise return NotReady
        let mut progress = false;
        let session_id = connected.session_id;

        while connected.inbound.len() < connected.inbound_max {
            match connected.tcp.poll() {
                Ok(Async::Ready(Some(msg))) => {
                    trace!("transport received msg");
                    progress = true;
                    connected.inbound.push_back(Message::Packet{session_id, content: msg});
                },
                Ok(Async::Ready(None)) => {
                    trace!("transport returned end of stream");

                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                },
                Ok(Async::NotReady) => {
                    trace!("transport not ready to read");
                    break;
                },
                Err(e) => {
                    trace!("transport read error: {:?}", e);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            };
        }

        while connected.outbound.len() < connected.outbound_max {
            match try!(connected.stream.poll()) {
                Async::Ready(Some(msg)) => {
                    progress = true;
                    connected.outbound.push_back(TimestampedItem::new(msg));
                },
                Async::Ready(None) => {
                    trace!("stream finished");
                    return Ok(Async::Ready(Finished(()).into()));
                },
                Async::NotReady => {
                    trace!("stream not ready to read");
                    break;
                }
            };
        }

        if let Some(TimestampedItem{ timestamp, item: sending }) = connected.outbound.pop_front() {
            match connected.tcp.start_send(sending) {
                Ok(AsyncSink::Ready) => {
                    trace!("transport wrote message");
                    progress = true;
                    connected.outbound_inflight = true;
                },
                Ok(AsyncSink::NotReady(sending)) => {
                    trace!("tcpstream not ready to write");

                    let timestamped_msg = TimestampedItem { timestamp: timestamp, item: sending };
                    if timestamped_msg.older_than(connected.outbound_max_age) {
                        trace!("message older than max age {:?}. reconnecting", timestamped_msg.age());

                        let connected = connected.take();

                        return Ok(Async::Ready(NotConnected {
                            handle: connected.handle,
                            stream: connected.stream,
                            sink: connected.sink,
                            error_count: connected.error_count,
                            session_id: connected.session_id + 1,
                            new_transport: connected.new_transport,
                            inbound_max: connected.inbound_max,
                            outbound_max: connected.outbound_max,
                            outbound_max_age: connected.outbound_max_age,
                        }.into()));
                    } else {
                        connected.outbound.push_front(timestamped_msg);
                    }
                },
                Err(err) => {
                    trace!("transport write error: {:?}", err);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            }
        }

        if let Some(msg) = connected.inbound.pop_front() {
            match try!(connected.sink.start_send(msg)) {
                AsyncSink::Ready => {
                    trace!("sink started send");
                    progress = true;
                    connected.inbound_inflight = true;
                },
                AsyncSink::NotReady(msg) => {
                    trace!("sink not ready");
                    connected.inbound.push_front(msg);
                },
            }
        }

        if connected.inbound_inflight {
            match try!(connected.sink.poll_complete()) {
                Async::Ready(()) => {
                    trace!("sink polled complete");
                    progress = true;
                    connected.inbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("sink polled not ready");
                    // do nothing
                }
            }
        }

        if connected.outbound_inflight {
            match try!(connected.tcp.poll_complete()) {
                Async::Ready(()) => {
                    trace!("transport poll complete");
                    progress = true;
                    connected.outbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("transport sink polled not ready");
                    // do nothing
                }
            }
        }

        if progress {
            trace!("made progress");

            let connected = connected.take();

            return Ok(Async::Ready(Connected {
                handle: connected.handle,
                stream: connected.stream,
                sink: connected.sink,
                tcp: connected.tcp,
                inbound: connected.inbound,
                inbound_max: connected.inbound_max,
                inbound_inflight: connected.inbound_inflight,
                outbound: connected.outbound,
                outbound_max: connected.outbound_max,
                outbound_max_age: connected.outbound_max_age,
                outbound_inflight: connected.outbound_inflight,
                error_count: connected.error_count,
                session_id: connected.session_id,
                new_transport: connected.new_transport,
            }.into()));
        } else {
            trace!("did not make progress");

            return Ok(Async::NotReady);
        }
    }
}
<commit_msg>wrap config params in a config struct<commit_after>use futures::{Async, AsyncSink, Future, Poll};
use futures::stream::Stream;
use futures::sink::Sink;
use state_machine_future::RentToOwn;
use tokio_core::reactor::Handle;
use tokio_retry::{self, Retry};
use tokio_retry::strategy::{FibonacciBackoff, jitter};
use std::io;
use std::error;
use std::time::{Duration, Instant};
use std::fmt;
use std::collections::VecDeque;

pub type ConnectionError = io::Error;
pub type SessionId = u32;

#[derive(Debug, Clone, PartialEq)]
pub struct Config {
    inbound_max: usize,
    outbound_max: usize,
    outbound_max_age: Duration,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Event {
    Connected {
        session_id: SessionId,
    }
}

pub enum Message<Item> {
    Packet {
        session_id: SessionId,
        content: Item,
    },
    Control {
        event: Event,
    }
}

pub struct TimestampedItem<Item> {
    pub timestamp: Instant,
    pub item: Item,
}

impl <Item> TimestampedItem<Item> {
    pub fn new(item: Item) -> TimestampedItem<Item> {
        TimestampedItem {
            timestamp: Instant::now(),
            item: item,
        }
    }

    pub fn age(&self) -> Duration {
        Instant::now().duration_since(self.timestamp)
    }

    pub fn older_than(&self, age: Duration) -> bool {
        self.age().gt(&age)
    }
}

impl <Item> Clone for Message<Item> where Item: Clone {
    fn clone(&self) -> Message<Item> {
        match self {
            &Message::Packet { session_id, ref content } => {
                Message::Packet {
                    session_id: session_id,
                    content: content.clone()
                }
            },
            &Message::Control { ref event } => {
                Message::Control {
                    event: event.clone()
                }
            }
        }
    }
}

impl <Item> fmt::Debug for Message<Item> where Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Packet { session_id, ref content } => {
                write!(f, "Message::Packet<session_id={:?}, content={:?}>", session_id, content)
            },
            &Message::Control { ref event } => {
                write!(f, "Message::Control<event={:?}>", event)
            }
        }
    }
}

pub trait NewTransport {
    type Future: Future<Item=Self::Transport,Error=Self::Error>;
    type Transport;
    type Error: error::Error + Sync + Send;

    fn new_transport(&self) -> Self::Future;
}

#[derive(StateMachineFuture)]
pub enum Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    #[state_machine_future(start, transitions(Connecting, Finished, Error))]
    NotConnected {
        handle: Handle,
        stream: S,
        sink: K,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        config: Config,
    },

    #[state_machine_future(transitions(NotConnected, Connected, Finished, Error))]
    Connecting {
        handle: Handle,
        stream: S,
        sink: K,
        tcp_future: Box<Future<Item=T,Error=io::Error>>,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        config: Config,
    },

    #[state_machine_future(transitions(Connected, NotConnected, Finished, Error))]
    Connected {
        handle: Handle,
        stream: S,
        sink: K,
        tcp: T,
        outbound: VecDeque<TimestampedItem<Item>>,
        outbound_inflight: bool,
        inbound: VecDeque<Message<Item>>,
        inbound_inflight: bool,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        config: Config,
    },

    #[state_machine_future(ready)]
    Finished(()),

    #[state_machine_future(error)]
    Error(ConnectionError),
}

impl <Item, S, K, T, N> Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    pub fn new(handle: Handle, stream: S, sink: K, new_transport: N, config: Config) -> ConnectionFuture<Item, S, K, T, N> {
        debug!("constructing new connection future");
        Connection::start(handle, stream, sink, 0, 0, new_transport, config)
    }
}

impl <Item, S, K, T, N> PollConnection<Item, S, K, T, N> for Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    fn poll_not_connected<'a>(not_connected: &'a mut RentToOwn<'a, NotConnected<Item, S, K, T, N>>) -> Poll<AfterNotConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("not connected");

        let retry_strategy = FibonacciBackoff ::from_millis(1)
            .max_delay(Duration::from_millis(2000))
            .map(jitter);

        let handle = not_connected.handle.clone();
        let new_transport = not_connected.new_transport.clone();

        let tcp_future = Box::new(Retry::spawn(handle, retry_strategy, move || {
            new_transport.new_transport()
        }).map_err(|e| {
            match e {
                tokio_retry::Error::OperationError(e) => io::Error::new(io::ErrorKind::Other, e),
                tokio_retry::Error::TimerError(e) => e,
            }
        })) as (Box<Future<Item=T,Error=io::Error>>);

        let not_connected = not_connected.take();
        Ok(Async::Ready(Connecting {
            handle: not_connected.handle,
            stream: not_connected.stream,
            sink: not_connected.sink,
            tcp_future: tcp_future,
            error_count: not_connected.error_count,
            session_id: not_connected.session_id,
            new_transport: not_connected.new_transport,
            config: not_connected.config,
        }.into()))
    }

    fn poll_connecting<'a>(connecting: &'a mut RentToOwn<'a, Connecting<Item, S, K, T, N>>) -> Poll<AfterConnecting<Item, S, K, T, N>, ConnectionError> {
        trace!("connecting");

        let tcp = try_ready!(connecting.tcp_future.poll());

        let connecting = connecting.take();
        Ok(Async::Ready(Connected {
            handle: connecting.handle,
            stream: connecting.stream,
            sink: connecting.sink,
            tcp: tcp,
            inbound: VecDeque::from(vec![
                Message::Control {
                    event: Event::Connected {
                        session_id: connecting.session_id
                    }
                }
            ]),
            inbound_inflight: false,
            outbound: VecDeque::new(),
            outbound_inflight: false,
            error_count: connecting.error_count,
            session_id: connecting.session_id,
            new_transport: connecting.new_transport,
            config: connecting.config,
        }.into()))
    }

    fn poll_connected<'a>(connected: &'a mut RentToOwn<'a, Connected<Item, S, K, T, N>>) -> Poll<AfterConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("connected");

        // we make progress if any poll returns Ready
        // if we make progress then we return a state or state change
        // otherwise return NotReady
        let mut progress = false;
        let session_id = connected.session_id;

        while connected.inbound.len() < connected.config.inbound_max {
            match connected.tcp.poll() {
                Ok(Async::Ready(Some(msg))) => {
                    trace!("transport received msg");
                    progress = true;
                    connected.inbound.push_back(Message::Packet{session_id, content: msg});
                },
                Ok(Async::Ready(None)) => {
                    trace!("transport returned end of stream");

                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        config: connected.config,
                    }.into()));
                },
                Ok(Async::NotReady) => {
                    trace!("transport not ready to read");
                    break;
                },
                Err(e) => {
                    trace!("transport read error: {:?}", e);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        config: connected.config,
                    }.into()));
                }
            };
        }

        while connected.outbound.len() < connected.config.outbound_max {
            match try!(connected.stream.poll()) {
                Async::Ready(Some(msg)) => {
                    progress = true;
                    connected.outbound.push_back(TimestampedItem::new(msg));
                },
                Async::Ready(None) => {
                    trace!("stream finished");
                    return Ok(Async::Ready(Finished(()).into()));
                },
                Async::NotReady => {
                    trace!("stream not ready to read");
                    break;
                }
            };
        }

        if let Some(TimestampedItem{ timestamp, item: sending }) = connected.outbound.pop_front() {
            match connected.tcp.start_send(sending) {
                Ok(AsyncSink::Ready) => {
                    trace!("transport wrote message");
                    progress = true;
                    connected.outbound_inflight = true;
                },
                Ok(AsyncSink::NotReady(sending)) => {
                    trace!("tcpstream not ready to write");

                    let timestamped_msg = TimestampedItem { timestamp: timestamp, item: sending };
                    if timestamped_msg.older_than(connected.config.outbound_max_age) {
                        trace!("message older than max age {:?}. reconnecting", timestamped_msg.age());

                        let connected = connected.take();

                        return Ok(Async::Ready(NotConnected {
                            handle: connected.handle,
                            stream: connected.stream,
                            sink: connected.sink,
                            error_count: connected.error_count,
                            session_id: connected.session_id + 1,
                            new_transport: connected.new_transport,
                            config: connected.config,
                        }.into()));
                    } else {
                        connected.outbound.push_front(timestamped_msg);
                    }
                },
                Err(err) => {
                    trace!("transport write error: {:?}", err);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        config: connected.config,
                    }.into()));
                }
            }
        }

        if let Some(msg) = connected.inbound.pop_front() {
            match try!(connected.sink.start_send(msg)) {
                AsyncSink::Ready => {
                    trace!("sink started send");
                    progress = true;
                    connected.inbound_inflight = true;
                },
                AsyncSink::NotReady(msg) => {
                    trace!("sink not ready");
                    connected.inbound.push_front(msg);
                },
            }
        }

        if connected.inbound_inflight {
            match try!(connected.sink.poll_complete()) {
                Async::Ready(()) => {
                    trace!("sink polled complete");
                    progress = true;
                    connected.inbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("sink polled not ready");
                    // do nothing
                }
            }
        }

        if connected.outbound_inflight {
            match try!(connected.tcp.poll_complete()) {
                Async::Ready(()) => {
                    trace!("transport poll complete");
                    progress = true;
                    connected.outbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("transport sink polled not ready");
                    // do nothing
                }
            }
        }

        if progress {
            trace!("made progress");

            let connected = connected.take();

            return Ok(Async::Ready(Connected {
                handle: connected.handle,
                stream: connected.stream,
                sink: connected.sink,
                tcp: connected.tcp,
                inbound: connected.inbound,
                inbound_inflight: connected.inbound_inflight,
                outbound: connected.outbound,
                outbound_inflight: connected.outbound_inflight,
                error_count: connected.error_count,
                session_id: connected.session_id,
                new_transport: connected.new_transport,
                config: connected.config,
            }.into()));
        } else {
            trace!("did not make progress");

            return Ok(Async::NotReady);
        }
    }
}
<|endoftext|>


<commit_before>import to_str::to_str;
import ptr::addr_of;
import comm::{port, chan, methods};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::net::url;
import std::net::url::url;
import std::uv_global_loop;
import comm::{methods};
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};
import request::build_request;

const timeout: uint = 2000;

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: ~str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+url: url) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, url)
}

#[allow(non_implicitly_copyable_typarams)]
class HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let url: url;
    let parser: Parser;
    let mut cb: fn@(+RequestEvent);

    new(resolver: DnsResolver, +connection_factory: CF, +url: url) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.url = url;
        self.parser = Parser();
        self.cb = |_event| { };
    }

    fn begin(cb: fn@(+RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = alt self.get_ip() {
          ok(ip_addr) => { copy ip_addr }
          err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::bytes(request_header);
        alt socket.write_(request_header_bytes) {
          result::ok(*) => { }
          result::err(e) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = next_data.get();
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                alt next_data {
                  result::err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> result<ip_addr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    alt ip {
                      ipv4(*) => { true }
                      ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut some(data));
        self.cb(the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(*events, event)
    }
    return *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        alt ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        alt ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<commit_msg>Changed alt to match<commit_after>import to_str::to_str;
import ptr::addr_of;
import comm::{port, chan, methods};
import result::{result, ok, err};
import std::net::ip::{
    get_addr, format_addr, ipv4, ipv6, ip_addr,
    ip_get_addr_err
};
import std::net::tcp::{connect, tcp_socket};
import std::net::url;
import std::net::url::url;
import std::uv_global_loop;
import comm::{methods};
import connection::{
    Connection, ConnectionFactory, UvConnectionFactory,
    MockConnection, MockConnectionFactory
};
import parser::{Parser, ParserCallbacks};
import request::build_request;

const timeout: uint = 2000;

/// HTTP status codes
enum StatusCode {
    StatusOk = 200,
    StatusFound = 302,
    StatusUnknown
}

/// HTTP request error conditions
enum RequestError {
    ErrorDnsResolution,
    ErrorConnect,
    ErrorMisc
}

/// Request
enum RequestEvent {
    Status(StatusCode),
    Payload(~mut option<~[u8]>),
    Error(RequestError)
}

type DnsResolver = fn@(host: ~str) -> result<~[ip_addr], ip_get_addr_err>;

fn uv_dns_resolver() -> DnsResolver {
    |host| {
        let iotask = uv_global_loop::get();
        get_addr(host, iotask)
    }
}

fn uv_http_request(+url: url) -> HttpRequest<tcp_socket, UvConnectionFactory> {
    HttpRequest(uv_dns_resolver(), UvConnectionFactory, url)
}

#[allow(non_implicitly_copyable_typarams)]
class HttpRequest<C: Connection, CF: ConnectionFactory<C>> {

    let resolve_ip_addr: DnsResolver;
    let connection_factory: CF;
    let url: url;
    let parser: Parser;
    let mut cb: fn@(+RequestEvent);

    new(resolver: DnsResolver, +connection_factory: CF, +url: url) {
        self.resolve_ip_addr = resolver;
        self.connection_factory = connection_factory;
        self.url = url;
        self.parser = Parser();
        self.cb = |_event| { };
    }

    fn begin(cb: fn@(+RequestEvent)) {
        #debug("http_client: looking up url %?", self.url.to_str());
        let ip_addr = match self.get_ip() {
          ok(ip_addr) => { copy ip_addr }
          err(e) => { cb(Error(e)); return }
        };

        #debug("http_client: using IP %? for %?", format_addr(ip_addr), self.url.to_str());

        let socket = {
            #debug("http_client: connecting to %?", ip_addr);
            let socket = self.connection_factory.connect(copy ip_addr, 80);
            if socket.is_ok() {
                result::unwrap(socket)
            } else {
                #debug("http_client: unable to connect to %?: %?", ip_addr, socket);
                cb(Error(ErrorConnect));
                return;
            }
        };

        #debug("http_client: got socket for %?", ip_addr);

        let request_header = build_request(self.url);
        #debug("http_client: writing request header: %?", request_header);
        let request_header_bytes = str::bytes(request_header);
        match socket.write_(request_header_bytes) {
          result::ok(*) => { }
          result::err(e) => {
            // FIXME: Need test
            cb(Error(ErrorMisc));
            return;
          }
        }

        let read_port = {
            let read_port = socket.read_start_();
            if read_port.is_ok() {
                result::unwrap(read_port)
            } else {
                cb(Error(ErrorMisc));
                return;
            }
        };

        // This unsafety is unfortunate but we can't capture self
        // into shared closures
        let unsafe_self = addr_of(self);
        let callbacks: ParserCallbacks = unsafe {{
            on_message_begin: || (*unsafe_self).on_message_begin(),
            on_url: |data| (*unsafe_self).on_url(data),
            on_header_field: |data| (*unsafe_self).on_header_field(data),
            on_header_value: |data| (*unsafe_self).on_header_value(data),
            on_headers_complete: || (*unsafe_self).on_headers_complete(),
            on_body: |data| (*unsafe_self).on_body(data),
            on_message_complete: || (*unsafe_self).on_message_complete()
        }};

        // Set the callback used by the parser event handlers
        self.cb = cb;

        loop {
            let next_data = read_port.recv();

            if next_data.is_ok() {
                let next_data = next_data.get();
                #debug("data: %?", next_data);
                let bytes_parsed = self.parser.execute(next_data, &callbacks);
                if bytes_parsed != next_data.len() {
                    // FIXME: Need tests
                    fail ~"http parse failure";
                }
            } else {
                #debug("http_client: read error: %?", next_data);

                // This method of detecting EOF is lame
                match next_data {
                  result::err({err_name: ~"EOF", _}) => {
                    self.parser.execute(~[], &callbacks);
                    break;
                  }
                  _ => {
                    // FIXME: Need tests and error handling
                    socket.read_stop_(read_port);
                    cb(Error(ErrorMisc));
                    return;
                  }
                }
            }
        }
        socket.read_stop_(read_port);
    }

    fn get_ip() -> result<ip_addr, RequestError> {
        let ip_addrs = self.resolve_ip_addr(self.url.host);
        if ip_addrs.is_ok() {
            let ip_addrs = result::unwrap(ip_addrs);
            // FIXME: This log crashes
            //#debug("http_client: got IP addresses for %?: %?", self.url, ip_addrs);
            if ip_addrs.is_not_empty() {
                // FIXME: Which address should we really pick?
                let best_ip = do ip_addrs.find |ip| {
                    match ip {
                      ipv4(*) => { true }
                      ipv6(*) => { false }
                    }
                };

                if best_ip.is_some() {
                    return ok(option::unwrap(best_ip));
                } else {
                    // FIXME: Need test
                    return err(ErrorMisc);
                }
            } else {
                #debug("http_client: got no IP addresses for %?", self.url);
                // FIXME: Need test
                return err(ErrorMisc);
            }
        } else {
            #debug("http_client: DNS lookup failure: %?", ip_addrs.get_err());
            return err(ErrorDnsResolution);
        }
    }

    fn on_message_begin() -> bool {
        #debug("on_message_begin");
        true
    }

    fn on_url(+_data: ~[u8]) -> bool {
        #debug("on_url");
        true
    }

    fn on_header_field(+data: ~[u8]) -> bool {
        let header_field = str::from_bytes(data);
        #debug("on_header_field: %?", header_field);
        true
    }

    fn on_header_value(+data: ~[u8]) -> bool {
        let header_value = str::from_bytes(data);
        #debug("on_header_value: %?", header_value);
        true
    }

    fn on_headers_complete() -> bool {
        #debug("on_headers_complete");
        true
    }

    fn on_body(+data: ~[u8]) -> bool {
        #debug("on_body");
        let the_payload = Payload(~mut some(data));
        self.cb(the_payload);
        true
    }

    fn on_message_complete() -> bool {
        #debug("on_message_complete");
        true
    }
}

#[allow(non_implicitly_copyable_typarams)]
fn sequence<C: Connection, CF: ConnectionFactory<C>>(request: HttpRequest<C, CF>) ->
    ~[RequestEvent] {

    let events = @mut ~[];
    do request.begin |event| {
        vec::push(*events, event)
    }
    return *events;
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_resolve_error() {
    let url = url::from_str(~"http://example.com_not_real/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorDnsResolution),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_error() {
    // This address is invalid because the first octet
    // of a class A address cannot be 0
    let url = url::from_str(~"http://0.42.42.42/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    assert events == ~[
        Error(ErrorConnect),
    ];
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_connect_success() {
    let url = url::from_str(~"http://example.com/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    for events.each |ev| {
        match ev {
          Error(*) => { fail }
          _ => { }
        }
    }
}

#[test]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_body() {
    let url = url::from_str(~"http://www.iana.org/").get();
    let request = uv_http_request(url);
    let events = sequence(request);

    let mut found = false;

    for events.each |ev| {
        match ev {
          Payload(value) => {
            if str::from_bytes(value.get()).contains(~"DOCTYPE html") {
                found = true
            }
          }
          _ => { }
        }
    }

    assert found;
}

#[test]
#[ignore(reason = "ICE")]
#[allow(non_implicitly_copyable_typarams)]
fn test_simple_response() {
    let _url = url::from_str(~"http://whatever/").get();
    let _mock_connection: MockConnection = {
        write_fn: |_data| { ok(()) },
        read_start_fn: || {
            let port = port();
            let chan = port.chan();

            let response = ~"HTTP/1.0 200 OK\
                            \
                            Test";
            chan.send(ok(str::bytes(response)));

            ok(port)
        },
        read_stop_fn: |_port| { ok(()) }
    };

    let _mock_connection_factory: MockConnectionFactory = {
        connect_fn: |_ip, _port| {

            // FIXME this doesn't work
            fail;//ok(mock_connection)
        }
    };
}
<|endoftext|>


<commit_before>extern crate ropey;

use std::io::Result;

use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use ropey::Rope;

/// This is the example from the front page of Ropey's documentation.
fn main() {
    do_stuff().unwrap();
}

/// Wrapper function, so we can use ? operator.
fn do_stuff() -> Result<()> {
    // Load the file into a Rope.
    let mut text = Rope::from_reader(BufReader::new(File::open("my_great_book.txt")?))?;

    // Make sure there are at least 516 lines.
    if text.len_lines() >= 516 {
        // Let's print the line first, to embarrass ourselves with our
        // terrible writing!  Note that lines are zero-indexed, so the
        // 516th line is at index 515.
        println!("{}", text.line(515));

        // Get the char indices of the start/end of the line.
        let start_idx = text.line_to_char(515);
        let end_idx = text.line_to_char(516);

        // Remove that terrible writing!
        text.remove(start_idx, end_idx);

        // ...and replace it with something better.
        text.insert(start_idx, "The flowers are... so... dunno.\n");

        // Let's print our changes, along with the previous few lines
        // for context.  Gotta make sure the writing works!
        let start_idx = text.line_to_char(511);
        let end_idx = text.line_to_char(516);
        println!("{}", text.slice(start_idx, end_idx));
    }

    // Write the file back out to disk.  We use the `Chunks` iterator
    // here to be maximally efficient.
    let mut file = BufWriter::new(File::create("my_great_book.txt")?);
    for chunk in text.chunks() {
        file.write(chunk.as_bytes())?;
    }

    Ok(())
}
<commit_msg>Removed duplicate example.<commit_after><|endoftext|>


<commit_before>#![feature(test)]

extern crate exonum;
extern crate test;

#[cfg(all(test, feature = "long_benchmarks"))]
#[cfg(test)]
mod tests {
    use test::Bencher;

    use std::net::SocketAddr;

    use exonum::node::EventsPoolCapacity;
    use exonum::events::network::NetworkConfiguration;
    use exonum::events::tests::{connect_message, raw_message, TestEvents, TestHandler};

    struct BenchConfig {
        times: usize,
        len: usize,
        tcp_nodelay: bool,
    }

    fn test_events(cfg: &BenchConfig, listen_address: SocketAddr) -> TestEvents {
        let network_config = NetworkConfiguration {
            tcp_nodelay: cfg.tcp_nodelay,
            ..Default::default()
        };
        TestEvents {
            listen_address,
            network_config,
            events_config: EventsPoolCapacity::default(),
        }
    }

    fn bench_network(b: &mut Bencher, addrs: [SocketAddr; 2], cfg: &BenchConfig) {
        b.iter(|| {
            let times = cfg.times;
            let len = cfg.len;

            let c1 = connect_message(addrs[0]);
            let c2 = connect_message(addrs[1]);

            let t1 = test_events(cfg, addrs[0]).spawn(move |e: &mut TestHandler| {
                e.connect_with(addrs[1]);
                assert_eq!(e.wait_for_connect(), c2);
                for _ in 0..times {
                    let msg = raw_message(0, len);
                    e.send_to(addrs[1], msg);
                    e.wait_for_message();
                }
                e.disconnect_with(addrs[1]);
                assert_eq!(e.wait_for_disconnect(), addrs[1]);
            });
            let t2 = test_events(cfg, addrs[1]).spawn(move |e: &mut TestHandler| {
                assert_eq!(e.wait_for_connect(), c1);
                e.connect_with(addrs[0]);
                for _ in 0..times {
                    let msg = raw_message(1, len);
                    e.send_to(addrs[0], msg);
                    e.wait_for_message();
                }
                assert_eq!(e.wait_for_disconnect(), addrs[0]);
            });

            t1.join().unwrap();
            t2.join().unwrap();
        })
    }

    #[bench]
    fn bench_msg_short_100(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100,
            times: 100,
        };
        let addrs = [
            "127.0.0.1:6990".parse().unwrap(), "127.0.0.1:6991".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_1000(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 1000,
            times: 1000,
        };
        let addrs = [
            "127.0.0.1:9792".parse().unwrap(), "127.0.0.1:9793".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_10000(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 1000,
            times: 10_000,
        };
        let addrs = [
            "127.0.0.1:9792".parse().unwrap(), "127.0.0.1:9793".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_100_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 100,
        };
        let addrs = [
            "127.0.0.1:4990".parse().unwrap(), "127.0.0.1:4991".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_1000_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 1000,
        };
        let addrs = [
            "127.0.0.1:5990".parse().unwrap(), "127.0.0.1:5991".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_10000_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 10_000,
        };
        let addrs = [
            "127.0.0.1:5990".parse().unwrap(), "127.0.0.1:5991".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_10(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100_000,
            times: 10,
        };
        let addrs = [
            "127.0.0.1:9984".parse().unwrap(), "127.0.0.1:9985".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_100(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100_000,
            times: 100,
        };
        let addrs = [
            "127.0.0.1:9946".parse().unwrap(), "127.0.0.1:9947".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_10_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100_000,
            times: 10,
        };
        let addrs = [
            "127.0.0.1:9198".parse().unwrap(), "127.0.0.1:9199".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_100_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100_000,
            times: 100,
        };
        let addrs = [
            "127.0.0.1:9198".parse().unwrap(), "127.0.0.1:9199".parse().unwrap()
        ];
        bench_network(b, addrs, &cfg);
    }
}
<commit_msg>Format code<commit_after>#![feature(test)]

extern crate exonum;
extern crate test;

#[cfg(all(test, feature = "long_benchmarks"))]
#[cfg(test)]
mod tests {
    use test::Bencher;

    use std::net::SocketAddr;

    use exonum::node::EventsPoolCapacity;
    use exonum::events::network::NetworkConfiguration;
    use exonum::events::tests::{connect_message, raw_message, TestEvents, TestHandler};

    struct BenchConfig {
        times: usize,
        len: usize,
        tcp_nodelay: bool,
    }

    fn test_events(cfg: &BenchConfig, listen_address: SocketAddr) -> TestEvents {
        let network_config = NetworkConfiguration {
            tcp_nodelay: cfg.tcp_nodelay,
            ..Default::default()
        };
        TestEvents {
            listen_address,
            network_config,
            events_config: EventsPoolCapacity::default(),
        }
    }

    fn bench_network(b: &mut Bencher, addrs: [SocketAddr; 2], cfg: &BenchConfig) {
        b.iter(|| {
            let times = cfg.times;
            let len = cfg.len;

            let c1 = connect_message(addrs[0]);
            let c2 = connect_message(addrs[1]);

            let t1 = test_events(cfg, addrs[0]).spawn(move |e: &mut TestHandler| {
                e.connect_with(addrs[1]);
                assert_eq!(e.wait_for_connect(), c2);
                for _ in 0..times {
                    let msg = raw_message(0, len);
                    e.send_to(addrs[1], msg);
                    e.wait_for_message();
                }
                e.disconnect_with(addrs[1]);
                assert_eq!(e.wait_for_disconnect(), addrs[1]);
            });
            let t2 = test_events(cfg, addrs[1]).spawn(move |e: &mut TestHandler| {
                assert_eq!(e.wait_for_connect(), c1);
                e.connect_with(addrs[0]);
                for _ in 0..times {
                    let msg = raw_message(1, len);
                    e.send_to(addrs[0], msg);
                    e.wait_for_message();
                }
                assert_eq!(e.wait_for_disconnect(), addrs[0]);
            });

            t1.join().unwrap();
            t2.join().unwrap();
        })
    }

    #[bench]
    fn bench_msg_short_100(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100,
            times: 100,
        };
        let addrs = ["127.0.0.1:6990".parse().unwrap(), "127.0.0.1:6991".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_1000(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 1000,
            times: 1000,
        };
        let addrs = ["127.0.0.1:9792".parse().unwrap(), "127.0.0.1:9793".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_10_000(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 1000,
            times: 10_000,
        };
        let addrs = ["127.0.0.1:9792".parse().unwrap(), "127.0.0.1:9793".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_100_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 100,
        };
        let addrs = ["127.0.0.1:4990".parse().unwrap(), "127.0.0.1:4991".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_1000_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 1000,
        };
        let addrs = ["127.0.0.1:5990".parse().unwrap(), "127.0.0.1:5991".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_short_10_000_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100,
            times: 10_000,
        };
        let addrs = ["127.0.0.1:5990".parse().unwrap(), "127.0.0.1:5991".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_10(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100_000,
            times: 10,
        };
        let addrs = ["127.0.0.1:9984".parse().unwrap(), "127.0.0.1:9985".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_100(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: false,
            len: 100_000,
            times: 100,
        };
        let addrs = ["127.0.0.1:9946".parse().unwrap(), "127.0.0.1:9947".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_10_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100_000,
            times: 10,
        };
        let addrs = ["127.0.0.1:9198".parse().unwrap(), "127.0.0.1:9199".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }

    #[bench]
    fn bench_msg_long_100_nodelay(b: &mut Bencher) {
        let cfg = BenchConfig {
            tcp_nodelay: true,
            len: 100_000,
            times: 100,
        };
        let addrs = ["127.0.0.1:9198".parse().unwrap(), "127.0.0.1:9199".parse().unwrap()];
        bench_network(b, addrs, &cfg);
    }
}
<|endoftext|>


<commit_before>use libucl_sys::*;
use libc::size_t;

use super::utils;
use super::object::Object;

use std::path::Path;

bitflags! {
    flags ParserFlags: i32 {
        const DEFAULT            = 0x0,
        const LOWERCASE          = 0x1,
        const ZEROCOPY           = 0x2,
        const NO_TIME            = 0x4,
        const NO_IMPLICIT_ARRAYS = 0x8
    }
}

pub struct Parser {
    parser: *mut ucl_parser,
}

impl Parser {
    /// Create new parser instance with default options
    pub fn new() -> Self {
        Self::with_flags(DEFAULT)
    }

    /// Create new parser with given option flags
    ///
    /// Flags:
    ///
    /// - `DEFAULT` - default configuration
    /// - `LOWERCASE` - convert all keys to lower case
    /// - `ZEROCOPY` - parse input in zero-copy mode if possible (you must ensure that input memory
    ///   is not freed if an object is in use)
    /// - `NO_TIME` - do not parse time and treat it's value as string
    /// - `NO_IMPLICIT_ARRAYS` - create explicit arrays instead of implicit ones
    ///
    /// # Examples
    ///
    /// ```rust
    /// let parser = ucl::Parser::with_flags(ucl::parser::LOWERCASE);
    /// let doc = parser.parse("A = b").unwrap();
    ///
    /// assert!(doc.fetch("a").is_some());
    /// ```
    pub fn with_flags(flags: ParserFlags) -> Self {
        Parser {
            parser: unsafe { ucl_parser_new(flags.bits()) }
        }
    }

    /// Parse given string. Returns root object on success.
    ///
    /// It moves out `Parser`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// assert!(ucl::Parser::new().parse("a = b").is_ok());
    /// assert!(ucl::Parser::new().parse("a =").is_err());
    /// ```
    pub fn parse<T: AsRef<str>>(mut self, string: T) -> Result<Object, String> {
        let len = string.as_ref().len() as size_t;
        let result = unsafe { ucl_parser_add_chunk(self.parser, utils::to_c_str(string), len) };

        if result {
            Ok(self.get_object().unwrap())
        } else {
            Err(self.get_error().unwrap())
        }
    }

    /// Parse file at given `Path`.
    ///
    /// It moves out `Parser`.
    pub fn parse_file<T: AsRef<Path>>(mut self, path: T) -> Result<Object, String> {
        let filename = path.as_ref().to_str().unwrap();
        let result = unsafe { ucl_parser_add_file(self.parser, utils::to_c_str(filename)) };

        if result {
            Ok(self.get_object().unwrap())
        } else {
            Err(self.get_error().unwrap())
        }
    }

    /// Register new variable
    ///
    /// # Examples
    ///
    /// ```rust
    /// let p = Parser::new();
    /// p.register_var("LOL".to_string(), "test".to_string());
    /// let res = p.parse("lol = $LOL").unwrap();
    ///
    /// assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string()));
    /// ```
    pub fn register_var(&self, name: String, value: String) {
        unsafe {
            ucl_parser_register_variable(self.parser, utils::to_c_str(name), utils::to_c_str(value))
        }
    }

    fn get_object(&mut self) -> Option<Object> {
        Object::from_cptr(unsafe { ucl_parser_get_object(self.parser) })
    }

    fn get_error(&mut self) -> Option<String> {
        let err = unsafe { ucl_parser_get_error(self.parser) };

        utils::to_str(err)
    }
}

impl Drop for Parser {
    fn drop(&mut self) {
        unsafe { ucl_parser_free(self.parser) }
    }
}

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

    #[test]
    fn string_parsing() {
        let p = Parser::new();
        let s = r#"lol = "lol""#;

        assert!(p.parse(s).is_ok());
    }

    #[test]
    fn empty_string_parsing() {
        let p = Parser::new();
        let s = r#""#;

        assert!(p.parse(s).is_ok());
    }

    #[test]
    fn key_fetching() {
        let p = Parser::new();
        let s = r#"lol = 10"#;
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10));
    }

    #[test]
    fn flags() {
        let s = r#"LoL = 10"#;
        let p = Parser::with_flags(DEFAULT);
        let res = p.parse(s).unwrap();

        assert!(res.fetch("lol").is_none());

        let p = Parser::with_flags(LOWERCASE);
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10));
    }

    #[test]
    fn variables() {
        let s = r#"lol = $LOL"#;
        let p = Parser::new();
        p.register_var("LOL".to_string(), "test".to_string());
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string()));
    }
}
<commit_msg>fix: forgotten module<commit_after>use libucl_sys::*;
use libc::size_t;

use super::utils;
use super::object::Object;

use std::path::Path;

bitflags! {
    flags ParserFlags: i32 {
        const DEFAULT            = 0x0,
        const LOWERCASE          = 0x1,
        const ZEROCOPY           = 0x2,
        const NO_TIME            = 0x4,
        const NO_IMPLICIT_ARRAYS = 0x8
    }
}

pub struct Parser {
    parser: *mut ucl_parser,
}

impl Parser {
    /// Create new parser instance with default options
    pub fn new() -> Self {
        Self::with_flags(DEFAULT)
    }

    /// Create new parser with given option flags
    ///
    /// Flags:
    ///
    /// - `DEFAULT` - default configuration
    /// - `LOWERCASE` - convert all keys to lower case
    /// - `ZEROCOPY` - parse input in zero-copy mode if possible (you must ensure that input memory
    ///   is not freed if an object is in use)
    /// - `NO_TIME` - do not parse time and treat it's value as string
    /// - `NO_IMPLICIT_ARRAYS` - create explicit arrays instead of implicit ones
    ///
    /// # Examples
    ///
    /// ```rust
    /// let parser = ucl::Parser::with_flags(ucl::parser::LOWERCASE);
    /// let doc = parser.parse("A = b").unwrap();
    ///
    /// assert!(doc.fetch("a").is_some());
    /// ```
    pub fn with_flags(flags: ParserFlags) -> Self {
        Parser {
            parser: unsafe { ucl_parser_new(flags.bits()) }
        }
    }

    /// Parse given string. Returns root object on success.
    ///
    /// It moves out `Parser`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// assert!(ucl::Parser::new().parse("a = b").is_ok());
    /// assert!(ucl::Parser::new().parse("a =").is_err());
    /// ```
    pub fn parse<T: AsRef<str>>(mut self, string: T) -> Result<Object, String> {
        let len = string.as_ref().len() as size_t;
        let result = unsafe { ucl_parser_add_chunk(self.parser, utils::to_c_str(string), len) };

        if result {
            Ok(self.get_object().unwrap())
        } else {
            Err(self.get_error().unwrap())
        }
    }

    /// Parse file at given `Path`.
    ///
    /// It moves out `Parser`.
    pub fn parse_file<T: AsRef<Path>>(mut self, path: T) -> Result<Object, String> {
        let filename = path.as_ref().to_str().unwrap();
        let result = unsafe { ucl_parser_add_file(self.parser, utils::to_c_str(filename)) };

        if result {
            Ok(self.get_object().unwrap())
        } else {
            Err(self.get_error().unwrap())
        }
    }

    /// Register new variable
    ///
    /// # Examples
    ///
    /// ```rust
    /// let p = ucl::Parser::new();
    /// p.register_var("LOL".to_string(), "test".to_string());
    /// let res = p.parse("lol = $LOL").unwrap();
    ///
    /// assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string()));
    /// ```
    pub fn register_var(&self, name: String, value: String) {
        unsafe {
            ucl_parser_register_variable(self.parser, utils::to_c_str(name), utils::to_c_str(value))
        }
    }

    fn get_object(&mut self) -> Option<Object> {
        Object::from_cptr(unsafe { ucl_parser_get_object(self.parser) })
    }

    fn get_error(&mut self) -> Option<String> {
        let err = unsafe { ucl_parser_get_error(self.parser) };

        utils::to_str(err)
    }
}

impl Drop for Parser {
    fn drop(&mut self) {
        unsafe { ucl_parser_free(self.parser) }
    }
}

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

    #[test]
    fn string_parsing() {
        let p = Parser::new();
        let s = r#"lol = "lol""#;

        assert!(p.parse(s).is_ok());
    }

    #[test]
    fn empty_string_parsing() {
        let p = Parser::new();
        let s = r#""#;

        assert!(p.parse(s).is_ok());
    }

    #[test]
    fn key_fetching() {
        let p = Parser::new();
        let s = r#"lol = 10"#;
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10));
    }

    #[test]
    fn flags() {
        let s = r#"LoL = 10"#;
        let p = Parser::with_flags(DEFAULT);
        let res = p.parse(s).unwrap();

        assert!(res.fetch("lol").is_none());

        let p = Parser::with_flags(LOWERCASE);
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10));
    }

    #[test]
    fn variables() {
        let s = r#"lol = $LOL"#;
        let p = Parser::new();
        p.register_var("LOL".to_string(), "test".to_string());
        let res = p.parse(s).unwrap();

        assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string()));
    }
}
<|endoftext|>