<commit_before>use crate::{
    channel::Channel,
    channels::Channels,
    configuration::Configuration,
    connection_closer::ConnectionCloser,
    connection_properties::ConnectionProperties,
    connection_status::{ConnectionState, ConnectionStatus, ConnectionStep},
    executor::{within_executor, DefaultExecutor, Executor},
    frames::Frames,
    internal_rpc::{InternalRPC, InternalRPCHandle},
    io_loop::IoLoop,
    reactor::DefaultReactorBuilder,
    socket_state::{SocketState, SocketStateHandle},
    tcp::{AMQPUriTcpExt, HandshakeResult, OwnedTLSConfig},
    thread::ThreadHandle,
    types::ShortUInt,
    uri::AMQPUri,
    Error, Promise, Result,
};
use amq_protocol::frame::{AMQPFrame, ProtocolVersion};
use async_trait::async_trait;
use log::{log_enabled, Level::Trace};
use std::{fmt, io, sync::Arc};

pub struct Connection {
    configuration: Configuration,
    status: ConnectionStatus,
    channels: Channels,
    io_loop: ThreadHandle,
    closer: Arc<ConnectionCloser>,
}

impl Connection {
    fn new(
        waker: SocketStateHandle,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
    ) -> Self {
        let configuration = Configuration::default();
        let status = ConnectionStatus::default();
        let channels = Channels::new(
            configuration.clone(),
            status.clone(),
            waker,
            internal_rpc.clone(),
            frames,
            executor,
        );
        let closer = Arc::new(ConnectionCloser::new(status.clone(), internal_rpc));
        let connection = Self {
            configuration,
            status,
            channels,
            io_loop: ThreadHandle::default(),
            closer,
        };

        connection.channels.create_zero();
        connection
    }

    /// Connect to an AMQP Server
    pub async fn connect(uri: &str, options: ConnectionProperties) -> Result<Connection> {
        Connect::connect(uri, options, OwnedTLSConfig::default()).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_with_config(
        uri: &str,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connect::connect(uri, options, config).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_uri(uri: AMQPUri, options: ConnectionProperties) -> Result<Connection> {
        Connect::connect(uri, options, OwnedTLSConfig::default()).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_uri_with_identity(
        uri: AMQPUri,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connect::connect(uri, options, config).await
    }

    pub async fn create_channel(&self) -> Result<Channel> {
        if !self.status.connected() {
            return Err(Error::InvalidConnectionState(self.status.state()));
        }
        let channel = self.channels.create(self.closer.clone())?;
        channel.clone().channel_open(channel).await
    }

    /// Block current thread while the connection is still active.
    /// This is useful when you only have a consumer and nothing else keeping your application
    /// "alive".
    pub fn run(self) -> Result<()> {
        let io_loop = self.io_loop.clone();
        drop(self);
        if within_executor() {
            // io_loop waits for the executor to shutdown, do not deadlock in the
            // unlikely case where run is called from within it
            Ok(())
        } else {
            io_loop.wait("io loop")
        }
    }

    pub fn on_error<E: FnMut(Error) + Send + 'static>(&self, handler: E) {
        self.channels.set_error_handler(handler);
    }

    pub fn configuration(&self) -> &Configuration {
        &self.configuration
    }

    pub fn status(&self) -> &ConnectionStatus {
        &self.status
    }

    pub async fn close(&self, reply_code: ShortUInt, reply_text: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0
                .connection_close(reply_code, reply_text, 0, 0)
                .await
        } else {
            Ok(())
        }
    }

    /// Block all consumers and publishers on this connection
    pub async fn block(&self, reason: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_blocked(reason).await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    /// Unblock all consumers and publishers on this connection
    pub async fn unblock(&self) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_unblocked().await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    /// Update the secret used by some authentication module such as OAuth2
    pub async fn update_secret(&self, new_secret: &str, reason: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_update_secret(new_secret, reason).await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    pub async fn connector(
        uri: AMQPUri,
        connect: Box<dyn FnOnce(&AMQPUri) -> HandshakeResult + Send + Sync>,
        mut options: ConnectionProperties,
    ) -> Result<Connection> {
        let executor = options
            .executor
            .take()
            .map(Ok)
            .unwrap_or_else(|| DefaultExecutor::default())?;

        let (connect_promise, resolver) = pinky_swear::PinkySwear::<HandshakeResult>::new();
        let connect_uri = uri.clone();
        executor.spawn_blocking(Box::new(move || {
            resolver.swear(connect(&connect_uri));
        }))?;

        let reactor_builder = options
            .reactor_builder
            .take()
            .unwrap_or_else(|| Arc::new(DefaultReactorBuilder));
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let frames = Frames::default();
        let conn = Connection::new(waker, internal_rpc.handle(), frames.clone(), executor);
        let status = conn.status.clone();
        let configuration = conn.configuration.clone();
        status.set_vhost(&uri.vhost);
        status.set_username(&uri.authority.userinfo.username);
        if let Some(frame_max) = uri.query.frame_max {
            configuration.set_frame_max(frame_max);
        }
        if let Some(channel_max) = uri.query.channel_max {
            configuration.set_channel_max(channel_max);
        }
        if let Some(heartbeat) = uri.query.heartbeat {
            configuration.set_heartbeat(heartbeat);
        }
        let (promise_out, resolver) = Promise::new();
        if log_enabled!(Trace) {
            promise_out.set_marker("ProtocolHeader".into());
        }
        let channels = conn.channels.clone();
        if let Some(channel0) = channels.get(0) {
            channel0.send_frame(
                AMQPFrame::ProtocolHeader(ProtocolVersion::amqp_0_9_1()),
                resolver,
                None,
            )
        };
        let (promise_in, resolver) = Promise::new();
        if log_enabled!(Trace) {
            promise_in.set_marker("ProtocolHeader.Ok".into());
        }
        let io_loop_handle = conn.io_loop.clone();
        status.set_state(ConnectionState::Connecting);
        status.set_connection_step(ConnectionStep::ProtocolHeader(
            resolver,
            conn,
            uri.authority.userinfo.into(),
            uri.query.auth_mechanism.unwrap_or_default(),
            options,
        ));
        let handshake_result = connect_promise.await;
        IoLoop::new(
            status,
            configuration,
            channels,
            internal_rpc,
            frames,
            socket_state,
            io_loop_handle,
            handshake_result,
            &*reactor_builder,
        )
        .and_then(IoLoop::start)?;
        promise_out.await?;
        promise_in.await
    }
}

impl fmt::Debug for Connection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Connection")
            .field("configuration", &self.configuration)
            .field("status", &self.status)
            .field("channels", &self.channels)
            .finish()
    }
}

/// Trait providing a method to connect to an AMQP server
#[async_trait]
pub trait Connect {
    /// connect to an AMQP server
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection>;
}

#[async_trait]
impl Connect for AMQPUri {
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connection::connector(
            self,
            Box::new(move |uri| AMQPUriTcpExt::connect_with_config(uri, config.as_ref())),
            options,
        )
        .await
    }
}

#[async_trait]
impl Connect for &str {
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        match self.parse::<AMQPUri>() {
            Ok(uri) => Connect::connect(uri, options, config).await,
            Err(err) => Err(io::Error::new(io::ErrorKind::Other, err).into()),
        }
    }
}

#[cfg(test)]
mod tests {
    use env_logger;

    use super::*;
    use crate::channel_receiver_state::ChannelReceiverState;
    use crate::channel_status::ChannelState;
    use crate::types::ShortString;
    use crate::BasicProperties;
    use amq_protocol::frame::AMQPContentHeader;
    use amq_protocol::protocol::{basic, AMQPClass};

    #[test]
    fn basic_consume_small_payload() {
        let _ = env_logger::try_init();

        use crate::consumer::Consumer;
        use crate::queue::{Queue, QueueState};

        // Bootstrap connection state to a consuming state
        let executor = DefaultExecutor::default().unwrap();
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let conn = Connection::new(
            waker,
            internal_rpc.handle(),
            Frames::default(),
            executor.clone(),
        );
        conn.status.set_state(ConnectionState::Connected);
        conn.configuration.set_channel_max(2047);
        let channel = conn.channels.create(conn.closer.clone()).unwrap();
        channel.set_state(ChannelState::Connected);
        let queue_name = ShortString::from("consumed");
        let mut queue: QueueState = Queue::new(queue_name.clone(), 0, 0).into();
        let consumer_tag = ShortString::from("consumer-tag");
        let consumer = Consumer::new(consumer_tag.clone(), executor);
        queue.register_consumer(consumer_tag.clone(), consumer);
        conn.channels
            .get(channel.id())
            .map(|c| c.register_queue(queue));
        // Now test the state machine behaviour
        {
            let method = AMQPClass::Basic(basic::AMQPMethod::Deliver(basic::Deliver {
                consumer_tag: consumer_tag.clone(),
                delivery_tag: 1,
                redelivered: false,
                exchange: "".into(),
                routing_key: queue_name.clone(),
            }));
            let class_id = method.get_amqp_class_id();
            let deliver_frame = AMQPFrame::Method(channel.id(), method);
            conn.channels.handle_frame(deliver_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::WillReceiveContent(
                class_id,
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let header_frame = AMQPFrame::Header(
                channel.id(),
                60,
                Box::new(AMQPContentHeader {
                    class_id: 60,
                    weight: 0,
                    body_size: 2,
                    properties: BasicProperties::default(),
                }),
            );
            conn.channels.handle_frame(header_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::ReceivingContent(
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
                2,
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let body_frame = AMQPFrame::Body(channel.id(), "{}".as_bytes().to_vec());
            conn.channels.handle_frame(body_frame).unwrap();
            let channel_state = channel.status().state();
            let expected_state = ChannelState::Connected;
            assert_eq!(channel_state, expected_state);
        }
    }

    #[test]
    fn basic_consume_empty_payload() {
        let _ = env_logger::try_init();

        use crate::consumer::Consumer;
        use crate::queue::{Queue, QueueState};

        // Bootstrap connection state to a consuming state
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let executor = DefaultExecutor::default().unwrap();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let conn = Connection::new(
            waker,
            internal_rpc.handle(),
            Frames::default(),
            executor.clone(),
        );
        conn.status.set_state(ConnectionState::Connected);
        conn.configuration.set_channel_max(2047);
        let channel = conn.channels.create(conn.closer.clone()).unwrap();
        channel.set_state(ChannelState::Connected);
        let queue_name = ShortString::from("consumed");
        let mut queue: QueueState = Queue::new(queue_name.clone(), 0, 0).into();
        let consumer_tag = ShortString::from("consumer-tag");
        let consumer = Consumer::new(consumer_tag.clone(), executor);
        queue.register_consumer(consumer_tag.clone(), consumer);
        conn.channels
            .get(channel.id())
            .map(|c| c.register_queue(queue));
        // Now test the state machine behaviour
        {
            let method = AMQPClass::Basic(basic::AMQPMethod::Deliver(basic::Deliver {
                consumer_tag: consumer_tag.clone(),
                delivery_tag: 1,
                redelivered: false,
                exchange: "".into(),
                routing_key: queue_name.clone(),
            }));
            let class_id = method.get_amqp_class_id();
            let deliver_frame = AMQPFrame::Method(channel.id(), method);
            conn.channels.handle_frame(deliver_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::WillReceiveContent(
                class_id,
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let header_frame = AMQPFrame::Header(
                channel.id(),
                60,
                Box::new(AMQPContentHeader {
                    class_id: 60,
                    weight: 0,
                    body_size: 0,
                    properties: BasicProperties::default(),
                }),
            );
            conn.channels.handle_frame(header_frame).unwrap();
            let channel_state = channel.status().state();
            let expected_state = ChannelState::Connected;
            assert_eq!(channel_state, expected_state);
        }
    }
}
<commit_msg>Connection: fix method name<commit_after>use crate::{
    channel::Channel,
    channels::Channels,
    configuration::Configuration,
    connection_closer::ConnectionCloser,
    connection_properties::ConnectionProperties,
    connection_status::{ConnectionState, ConnectionStatus, ConnectionStep},
    executor::{within_executor, DefaultExecutor, Executor},
    frames::Frames,
    internal_rpc::{InternalRPC, InternalRPCHandle},
    io_loop::IoLoop,
    reactor::DefaultReactorBuilder,
    socket_state::{SocketState, SocketStateHandle},
    tcp::{AMQPUriTcpExt, HandshakeResult, OwnedTLSConfig},
    thread::ThreadHandle,
    types::ShortUInt,
    uri::AMQPUri,
    Error, Promise, Result,
};
use amq_protocol::frame::{AMQPFrame, ProtocolVersion};
use async_trait::async_trait;
use log::{log_enabled, Level::Trace};
use std::{fmt, io, sync::Arc};

pub struct Connection {
    configuration: Configuration,
    status: ConnectionStatus,
    channels: Channels,
    io_loop: ThreadHandle,
    closer: Arc<ConnectionCloser>,
}

impl Connection {
    fn new(
        waker: SocketStateHandle,
        internal_rpc: InternalRPCHandle,
        frames: Frames,
        executor: Arc<dyn Executor>,
    ) -> Self {
        let configuration = Configuration::default();
        let status = ConnectionStatus::default();
        let channels = Channels::new(
            configuration.clone(),
            status.clone(),
            waker,
            internal_rpc.clone(),
            frames,
            executor,
        );
        let closer = Arc::new(ConnectionCloser::new(status.clone(), internal_rpc));
        let connection = Self {
            configuration,
            status,
            channels,
            io_loop: ThreadHandle::default(),
            closer,
        };

        connection.channels.create_zero();
        connection
    }

    /// Connect to an AMQP Server
    pub async fn connect(uri: &str, options: ConnectionProperties) -> Result<Connection> {
        Connect::connect(uri, options, OwnedTLSConfig::default()).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_with_config(
        uri: &str,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connect::connect(uri, options, config).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_uri(uri: AMQPUri, options: ConnectionProperties) -> Result<Connection> {
        Connect::connect(uri, options, OwnedTLSConfig::default()).await
    }

    /// Connect to an AMQP Server
    pub async fn connect_uri_with_config(
        uri: AMQPUri,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connect::connect(uri, options, config).await
    }

    pub async fn create_channel(&self) -> Result<Channel> {
        if !self.status.connected() {
            return Err(Error::InvalidConnectionState(self.status.state()));
        }
        let channel = self.channels.create(self.closer.clone())?;
        channel.clone().channel_open(channel).await
    }

    /// Block current thread while the connection is still active.
    /// This is useful when you only have a consumer and nothing else keeping your application
    /// "alive".
    pub fn run(self) -> Result<()> {
        let io_loop = self.io_loop.clone();
        drop(self);
        if within_executor() {
            // io_loop waits for the executor to shutdown, do not deadlock in the
            // unlikely case where run is called from within it
            Ok(())
        } else {
            io_loop.wait("io loop")
        }
    }

    pub fn on_error<E: FnMut(Error) + Send + 'static>(&self, handler: E) {
        self.channels.set_error_handler(handler);
    }

    pub fn configuration(&self) -> &Configuration {
        &self.configuration
    }

    pub fn status(&self) -> &ConnectionStatus {
        &self.status
    }

    pub async fn close(&self, reply_code: ShortUInt, reply_text: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0
                .connection_close(reply_code, reply_text, 0, 0)
                .await
        } else {
            Ok(())
        }
    }

    /// Block all consumers and publishers on this connection
    pub async fn block(&self, reason: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_blocked(reason).await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    /// Unblock all consumers and publishers on this connection
    pub async fn unblock(&self) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_unblocked().await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    /// Update the secret used by some authentication module such as OAuth2
    pub async fn update_secret(&self, new_secret: &str, reason: &str) -> Result<()> {
        if let Some(channel0) = self.channels.get(0) {
            channel0.connection_update_secret(new_secret, reason).await
        } else {
            Err(Error::InvalidConnectionState(self.status.state()))
        }
    }

    pub async fn connector(
        uri: AMQPUri,
        connect: Box<dyn FnOnce(&AMQPUri) -> HandshakeResult + Send + Sync>,
        mut options: ConnectionProperties,
    ) -> Result<Connection> {
        let executor = options
            .executor
            .take()
            .map(Ok)
            .unwrap_or_else(|| DefaultExecutor::default())?;

        let (connect_promise, resolver) = pinky_swear::PinkySwear::<HandshakeResult>::new();
        let connect_uri = uri.clone();
        executor.spawn_blocking(Box::new(move || {
            resolver.swear(connect(&connect_uri));
        }))?;

        let reactor_builder = options
            .reactor_builder
            .take()
            .unwrap_or_else(|| Arc::new(DefaultReactorBuilder));
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let frames = Frames::default();
        let conn = Connection::new(waker, internal_rpc.handle(), frames.clone(), executor);
        let status = conn.status.clone();
        let configuration = conn.configuration.clone();
        status.set_vhost(&uri.vhost);
        status.set_username(&uri.authority.userinfo.username);
        if let Some(frame_max) = uri.query.frame_max {
            configuration.set_frame_max(frame_max);
        }
        if let Some(channel_max) = uri.query.channel_max {
            configuration.set_channel_max(channel_max);
        }
        if let Some(heartbeat) = uri.query.heartbeat {
            configuration.set_heartbeat(heartbeat);
        }
        let (promise_out, resolver) = Promise::new();
        if log_enabled!(Trace) {
            promise_out.set_marker("ProtocolHeader".into());
        }
        let channels = conn.channels.clone();
        if let Some(channel0) = channels.get(0) {
            channel0.send_frame(
                AMQPFrame::ProtocolHeader(ProtocolVersion::amqp_0_9_1()),
                resolver,
                None,
            )
        };
        let (promise_in, resolver) = Promise::new();
        if log_enabled!(Trace) {
            promise_in.set_marker("ProtocolHeader.Ok".into());
        }
        let io_loop_handle = conn.io_loop.clone();
        status.set_state(ConnectionState::Connecting);
        status.set_connection_step(ConnectionStep::ProtocolHeader(
            resolver,
            conn,
            uri.authority.userinfo.into(),
            uri.query.auth_mechanism.unwrap_or_default(),
            options,
        ));
        let handshake_result = connect_promise.await;
        IoLoop::new(
            status,
            configuration,
            channels,
            internal_rpc,
            frames,
            socket_state,
            io_loop_handle,
            handshake_result,
            &*reactor_builder,
        )
        .and_then(IoLoop::start)?;
        promise_out.await?;
        promise_in.await
    }
}

impl fmt::Debug for Connection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Connection")
            .field("configuration", &self.configuration)
            .field("status", &self.status)
            .field("channels", &self.channels)
            .finish()
    }
}

/// Trait providing a method to connect to an AMQP server
#[async_trait]
pub trait Connect {
    /// connect to an AMQP server
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection>;
}

#[async_trait]
impl Connect for AMQPUri {
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        Connection::connector(
            self,
            Box::new(move |uri| AMQPUriTcpExt::connect_with_config(uri, config.as_ref())),
            options,
        )
        .await
    }
}

#[async_trait]
impl Connect for &str {
    async fn connect(
        self,
        options: ConnectionProperties,
        config: OwnedTLSConfig,
    ) -> Result<Connection> {
        match self.parse::<AMQPUri>() {
            Ok(uri) => Connect::connect(uri, options, config).await,
            Err(err) => Err(io::Error::new(io::ErrorKind::Other, err).into()),
        }
    }
}

#[cfg(test)]
mod tests {
    use env_logger;

    use super::*;
    use crate::channel_receiver_state::ChannelReceiverState;
    use crate::channel_status::ChannelState;
    use crate::types::ShortString;
    use crate::BasicProperties;
    use amq_protocol::frame::AMQPContentHeader;
    use amq_protocol::protocol::{basic, AMQPClass};

    #[test]
    fn basic_consume_small_payload() {
        let _ = env_logger::try_init();

        use crate::consumer::Consumer;
        use crate::queue::{Queue, QueueState};

        // Bootstrap connection state to a consuming state
        let executor = DefaultExecutor::default().unwrap();
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let conn = Connection::new(
            waker,
            internal_rpc.handle(),
            Frames::default(),
            executor.clone(),
        );
        conn.status.set_state(ConnectionState::Connected);
        conn.configuration.set_channel_max(2047);
        let channel = conn.channels.create(conn.closer.clone()).unwrap();
        channel.set_state(ChannelState::Connected);
        let queue_name = ShortString::from("consumed");
        let mut queue: QueueState = Queue::new(queue_name.clone(), 0, 0).into();
        let consumer_tag = ShortString::from("consumer-tag");
        let consumer = Consumer::new(consumer_tag.clone(), executor);
        queue.register_consumer(consumer_tag.clone(), consumer);
        conn.channels
            .get(channel.id())
            .map(|c| c.register_queue(queue));
        // Now test the state machine behaviour
        {
            let method = AMQPClass::Basic(basic::AMQPMethod::Deliver(basic::Deliver {
                consumer_tag: consumer_tag.clone(),
                delivery_tag: 1,
                redelivered: false,
                exchange: "".into(),
                routing_key: queue_name.clone(),
            }));
            let class_id = method.get_amqp_class_id();
            let deliver_frame = AMQPFrame::Method(channel.id(), method);
            conn.channels.handle_frame(deliver_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::WillReceiveContent(
                class_id,
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let header_frame = AMQPFrame::Header(
                channel.id(),
                60,
                Box::new(AMQPContentHeader {
                    class_id: 60,
                    weight: 0,
                    body_size: 2,
                    properties: BasicProperties::default(),
                }),
            );
            conn.channels.handle_frame(header_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::ReceivingContent(
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
                2,
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let body_frame = AMQPFrame::Body(channel.id(), "{}".as_bytes().to_vec());
            conn.channels.handle_frame(body_frame).unwrap();
            let channel_state = channel.status().state();
            let expected_state = ChannelState::Connected;
            assert_eq!(channel_state, expected_state);
        }
    }

    #[test]
    fn basic_consume_empty_payload() {
        let _ = env_logger::try_init();

        use crate::consumer::Consumer;
        use crate::queue::{Queue, QueueState};

        // Bootstrap connection state to a consuming state
        let socket_state = SocketState::default();
        let waker = socket_state.handle();
        let executor = DefaultExecutor::default().unwrap();
        let internal_rpc = InternalRPC::new(executor.clone(), waker.clone());
        let conn = Connection::new(
            waker,
            internal_rpc.handle(),
            Frames::default(),
            executor.clone(),
        );
        conn.status.set_state(ConnectionState::Connected);
        conn.configuration.set_channel_max(2047);
        let channel = conn.channels.create(conn.closer.clone()).unwrap();
        channel.set_state(ChannelState::Connected);
        let queue_name = ShortString::from("consumed");
        let mut queue: QueueState = Queue::new(queue_name.clone(), 0, 0).into();
        let consumer_tag = ShortString::from("consumer-tag");
        let consumer = Consumer::new(consumer_tag.clone(), executor);
        queue.register_consumer(consumer_tag.clone(), consumer);
        conn.channels
            .get(channel.id())
            .map(|c| c.register_queue(queue));
        // Now test the state machine behaviour
        {
            let method = AMQPClass::Basic(basic::AMQPMethod::Deliver(basic::Deliver {
                consumer_tag: consumer_tag.clone(),
                delivery_tag: 1,
                redelivered: false,
                exchange: "".into(),
                routing_key: queue_name.clone(),
            }));
            let class_id = method.get_amqp_class_id();
            let deliver_frame = AMQPFrame::Method(channel.id(), method);
            conn.channels.handle_frame(deliver_frame).unwrap();
            let channel_state = channel.status().receiver_state();
            let expected_state = ChannelReceiverState::WillReceiveContent(
                class_id,
                Some(queue_name.clone()),
                Some(consumer_tag.clone()),
            );
            assert_eq!(channel_state, expected_state);
        }
        {
            let header_frame = AMQPFrame::Header(
                channel.id(),
                60,
                Box::new(AMQPContentHeader {
                    class_id: 60,
                    weight: 0,
                    body_size: 0,
                    properties: BasicProperties::default(),
                }),
            );
            conn.channels.handle_frame(header_frame).unwrap();
            let channel_state = channel.status().state();
            let expected_state = ChannelState::Connected;
            assert_eq!(channel_state, expected_state);
        }
    }
}
<|endoftext|>


<commit_before>use glium::backend::glutin_backend::GlutinFacade;
use glium::DisplayBuild;
use glium::glutin::{VirtualKeyCode, ElementState};
use gpu::renderer::{GLRenderer, Renderer};
use gb_proc::handler_holder::Key;
use gb_proc::video_controller::ScreenBuffer;
use gb_proc::cpu::Interrupt;

use glium;
use glium::glutin;

pub struct Controller {
    display: GlutinFacade,
    renderer: GLRenderer,
}

pub enum Event {
    Quit,
    Break,
    ToggleSpeed,
    Continue,
}

impl Controller {
    pub fn new() -> Controller {
        let mut display = glium::glutin::WindowBuilder::new()
            .with_dimensions(640, 576)
            .build_glium()
            .unwrap();

        let renderer = GLRenderer::new(&mut display);

        Controller {
            display: display,
            renderer: renderer,
        }
    }

    pub fn check_events(&mut self, hardware: &mut Hardware) -> Event {
        for event in self.display.poll_events() {
            match event {
                glutin::Event::Closed => {
                    return Event::Quit;
                },
                glutin::Event::KeyboardInput(state, _, virtual_key) => {
                    if state == ElementState::Pressed {
                        match virtual_key {
                            Some(VirtualKeyCode::F1) => {
                                return Event::Break;
                            },
                            Some(VirtualKeyCode::F2) => {
                                if state == ElementState::Pressed {
                                    return Event::ToggleSpeed;
                                }
                            },
                            _ => {},
                        }
                    }

                    let key = virtual_key.and_then(|k| match k {
                        VirtualKeyCode::Left  => Some(Key::Left),
                        VirtualKeyCode::Right => Some(Key::Right),
                        VirtualKeyCode::Up    => Some(Key::Up),
                        VirtualKeyCode::Down  => Some(Key::Down),
                        VirtualKeyCode::A     => Some(Key::A),
                        VirtualKeyCode::S     => Some(Key::B),
                        VirtualKeyCode::D     => Some(Key::Start),
                        VirtualKeyCode::F     => Some(Key::Select),
                        _ => None,
                    });

                    if let Some(k) = key {
                        match state {
                            ElementState::Pressed => { hardware.key_down(k) },
                            ElementState::Released => { hardware.key_up(k) },
                        }
                        hardware.interrupt(Interrupt::Joypad);
                    };
                },
                _ => {}
            }
        };

        Event::Continue
    }
}

impl Renderer for Controller {
    fn refresh(&mut self, pixels: &ScreenBuffer) {
        let mut frame = self.display.draw();
        self.renderer.refresh(&mut frame, pixels);
        frame.finish().unwrap();
    }
}

pub trait Hardware {
    fn get_screen_buffer(&self) -> &ScreenBuffer;
    fn interrupt(&mut self, interrupt: Interrupt);
    fn key_up(&mut self, key: Key);
    fn key_down(&mut self, key: Key);
    fn next(&mut self);
}
<commit_msg>Add title to the window.<commit_after>use glium::backend::glutin_backend::GlutinFacade;
use glium::DisplayBuild;
use glium::glutin::{VirtualKeyCode, ElementState};
use gpu::renderer::{GLRenderer, Renderer};
use gb_proc::handler_holder::Key;
use gb_proc::video_controller::ScreenBuffer;
use gb_proc::cpu::Interrupt;

use glium;
use glium::glutin;

pub struct Controller {
    display: GlutinFacade,
    renderer: GLRenderer,
}

pub enum Event {
    Quit,
    Break,
    ToggleSpeed,
    Continue,
}

impl Controller {
    pub fn new() -> Controller {
        let mut display = glium::glutin::WindowBuilder::new()
            .with_title("gb-rust")
            .with_dimensions(640, 576)
            .build_glium()
            .unwrap();

        let renderer = GLRenderer::new(&mut display);

        Controller {
            display: display,
            renderer: renderer,
        }
    }

    pub fn check_events(&mut self, hardware: &mut Hardware) -> Event {
        for event in self.display.poll_events() {
            match event {
                glutin::Event::Closed => {
                    return Event::Quit;
                },
                glutin::Event::KeyboardInput(state, _, virtual_key) => {
                    if state == ElementState::Pressed {
                        match virtual_key {
                            Some(VirtualKeyCode::F1) => {
                                return Event::Break;
                            },
                            Some(VirtualKeyCode::F2) => {
                                if state == ElementState::Pressed {
                                    return Event::ToggleSpeed;
                                }
                            },
                            _ => {},
                        }
                    }

                    let key = virtual_key.and_then(|k| match k {
                        VirtualKeyCode::Left  => Some(Key::Left),
                        VirtualKeyCode::Right => Some(Key::Right),
                        VirtualKeyCode::Up    => Some(Key::Up),
                        VirtualKeyCode::Down  => Some(Key::Down),
                        VirtualKeyCode::A     => Some(Key::A),
                        VirtualKeyCode::S     => Some(Key::B),
                        VirtualKeyCode::D     => Some(Key::Start),
                        VirtualKeyCode::F     => Some(Key::Select),
                        _ => None,
                    });

                    if let Some(k) = key {
                        match state {
                            ElementState::Pressed => { hardware.key_down(k) },
                            ElementState::Released => { hardware.key_up(k) },
                        }
                        hardware.interrupt(Interrupt::Joypad);
                    };
                },
                _ => {}
            }
        };

        Event::Continue
    }
}

impl Renderer for Controller {
    fn refresh(&mut self, pixels: &ScreenBuffer) {
        let mut frame = self.display.draw();
        self.renderer.refresh(&mut frame, pixels);
        frame.finish().unwrap();
    }
}

pub trait Hardware {
    fn get_screen_buffer(&self) -> &ScreenBuffer;
    fn interrupt(&mut self, interrupt: Interrupt);
    fn key_up(&mut self, key: Key);
    fn key_down(&mut self, key: Key);
    fn next(&mut self);
}
<|endoftext|>


<commit_before>use hyper::{Uri, Client, Method, Error};
use hyper::header::UserAgent;
use hyper::client::{Request, FutureResponse};
use hyper_tls::HttpsConnector;
use futures::{Future, Stream};
use futures::future;
use serde_json;
use serde::Deserialize;

use std::fs::{File, OpenOptions}; // TODO file writing to utils.rs
use std::io::Write;
use std::path::Path;

use curl::easy::Easy;
use utils::{parse_url_from_str, log_response_status};
use utils;
use models::*;
use endpoint::HnNewsEndpoint;
use app::{AppDomain, AppStates, AppStateMachine};

pub fn get_top_story_ids(app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Result<HnListOfItems, Error> {
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_top_stories_path(), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnListOfItems>(chunks));
    let result = app_domain.core.run(work);
    state.current_state = AppStates::DoingLocalWork;
    result
}

fn deserialize<T: Deserialize>(chunks: Vec<u8>) -> T {
    let s = String::from_utf8(chunks).unwrap();
    let deserialized: T = serde_json::from_str(&s).unwrap();
    deserialized
}

///
/// Gets HnItem wrapped in Result
///
pub fn get_item_by_id(item: &str,
                      app_domain: &mut AppDomain,
                      state: &mut AppStateMachine)
                      -> Result<HnItem, Error> {
    // note kids in item are comments, parts not sure what it is
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_item(&item, &client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_item_path(&item), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnItem>(chunks));
    state.current_state = AppStates::DoingLocalWork;
    let result = app_domain.core.run(work);
    result
}

pub fn get_comments_for_item(item: &HnItem,
                         app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Option<Vec<HnItem>> {
    match item.kids {
        Some(ref kids) => {
            let core = &mut app_domain.core;
            let logger = &mut app_domain.logger; // These need to be mutable as otherwise it'll cause mutable<>immutable borrow error
            let endpoint = &mut app_domain.endpoint;
            let client = &mut app_domain.client;
            state.current_state = AppStates::RetrievingResults;

            let comments: &Vec<i32> = &kids;
            info!(&logger,
                  format!("Retrieving comments for {} with {} comments",
                          &item.id,
                          kids.len()));
            let items = comments.iter()
                .map(|item_id| {
                    (item_id.to_string(), request_item(&item_id.to_string(), &client, &endpoint))
                })
                .map(|(item_id, request_work)| {
                    let subtask = request_work.and_then(|response| {
                        response.body()
                            .fold(Vec::new(), |mut v, chunk| {
                                v.extend(&chunk[..]);
                                future::ok::<_, Error>(v)
                            })
                    });
                    core.run(subtask).unwrap()
                })
                .collect::<Vec<Vec<u8>>>()
                .into_iter()
                .map(|chunks| deserialize::<HnItem>(chunks))
                .collect::<Vec<HnItem>>();
            state.current_state = AppStates::DoingLocalWork;
            Some(items)
        }
        None => None,
    }
}

fn request_top_story_ids(client: &Client<HttpsConnector>,
                         endpoints: &HnNewsEndpoint)
                         -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_top_stories_path());
    create_get_request(url, &client)
}

fn request_best_stories_ids(client: &Client<HttpsConnector>,
                            endpoints: &HnNewsEndpoint)
                            -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_best_stories_path());
    create_get_request(url, &client)
}

fn request_item(item: &str,
                client: &Client<HttpsConnector>,
                endpoints: &HnNewsEndpoint)
                -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_item_path(item));
    create_get_request(url, &client)
}

pub fn download_page_from_item(item: &HnItem,
                               app_domain: &mut AppDomain,
                               state: &mut AppStateMachine)
                               -> Result<String, String> {
    match item.url {
        // todo change to async
        Some(ref url) => {
            let uri = utils::parse_url_from_str(url);
            let filename: String = utils::filename_for_hnitem(&item);
            let path = Path::new(&filename);
            let mut file: File =
                OpenOptions::new().write(true).create(true).open(path.as_os_str()).unwrap();

            let v = curl_req(url);
            let s = String::from_utf8(v).unwrap(); // TODO errors
            file.write_all(s.as_bytes());
            Ok(String::from(path.as_os_str().to_str().unwrap())) // should probably be path to string
        }
        None => Err(String::from("No url")),
    }

}

fn curl_req(url: &String) -> Vec<u8> {
    let mut vecced: Vec<u8> = Vec::new();
    let mut easy = Easy::new();
    easy.get(true).unwrap();
    easy.url(url).unwrap();
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
                vecced.extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
        transfer.perform().unwrap();
    }
    vecced
}

fn create_get_request(url: Uri, client: &Client<HttpsConnector>) -> FutureResponse {
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("rs-hackernews-cli"));
}

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

    #[test]
    fn request_item_test() {
        let mut app_domain = AppDomain::new();
        let s = String::from("8000");
        let response = app_domain.core
            .run(request_item(&s, &app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_top_story_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_best_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_best_stories_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }

    #[test]
    fn get_item_by_id_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("126809");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        assert!(hnitem.score.unwrap() != 0);
        assert!(hnitem.id != 0);
        assert!(!hnitem.type_str.is_empty());
        assert!(!hnitem.title.unwrap().is_empty());
        assert_eq!(126809, hnitem.id);
    }
    #[test]
    fn get_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let top_stories: HnListOfItems = get_top_story_ids(&mut app_domain, &mut app_sm).unwrap();
        assert!(top_stories.values.len() != 0);
    }

    #[test]
    fn get_comments_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("14114235");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        let comments: Vec<HnItem> = get_comments_for_item(&hnitem, &mut app_domain, &mut app_sm)
            .unwrap();
        assert!(comments.len() != 0);
    }
}
<commit_msg>Removes the comments with empty text<commit_after>use hyper::{Uri, Client, Method, Error};
use hyper::header::UserAgent;
use hyper::client::{Request, FutureResponse};
use hyper_tls::HttpsConnector;
use futures::{Future, Stream};
use futures::future;
use serde_json;
use serde::Deserialize;

use std::fs::{File, OpenOptions}; // TODO file writing to utils.rs
use std::io::Write;
use std::path::Path;

use curl::easy::Easy;
use utils::{parse_url_from_str, log_response_status};
use utils;
use models::*;
use endpoint::HnNewsEndpoint;
use app::{AppDomain, AppStates, AppStateMachine};

pub fn get_top_story_ids(app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Result<HnListOfItems, Error> {
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_top_stories_path(), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnListOfItems>(chunks));
    let result = app_domain.core.run(work);
    state.current_state = AppStates::DoingLocalWork;
    result
}

fn deserialize<T: Deserialize>(chunks: Vec<u8>) -> T {
    let s = String::from_utf8(chunks).unwrap();
    let deserialized: T = serde_json::from_str(&s).unwrap();
    deserialized
}

///
/// Gets HnItem wrapped in Result
///
pub fn get_item_by_id(item: &str,
                      app_domain: &mut AppDomain,
                      state: &mut AppStateMachine)
                      -> Result<HnItem, Error> {
    // note kids in item are comments, parts not sure what it is
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_item(&item, &client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_item_path(&item), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnItem>(chunks));
    state.current_state = AppStates::DoingLocalWork;
    let result = app_domain.core.run(work);
    result
}

pub fn get_comments_for_item(item: &HnItem,
                         app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Option<Vec<HnItem>> {
    match item.kids {
        Some(ref kids) => {
            let core = &mut app_domain.core;
            let logger = &mut app_domain.logger; // These need to be mutable as otherwise it'll cause mutable<>immutable borrow error
            let endpoint = &mut app_domain.endpoint;
            let client = &mut app_domain.client;
            state.current_state = AppStates::RetrievingResults;

            let comments: &Vec<i32> = &kids;
            info!(&logger,
                  format!("Retrieving comments for {} with {} comments",
                          &item.id,
                          kids.len()));
            let items = comments.iter()
                .map(|item_id| {
                    (item_id.to_string(), request_item(&item_id.to_string(), &client, &endpoint))
                })
                .map(|(item_id, request_work)| {
                    let subtask = request_work.and_then(|response| {
                        response.body()
                            .fold(Vec::new(), |mut v, chunk| {
                                v.extend(&chunk[..]);
                                future::ok::<_, Error>(v)
                            })
                    });
                    core.run(subtask).unwrap()
                })
                .collect::<Vec<Vec<u8>>>()
                .into_iter()
                .map(|chunks| deserialize::<HnItem>(chunks))
                .filter(|item:&HnItem| item.text.is_some()) // for some reason there are comments which have no text
                .collect::<Vec<HnItem>>();
            state.current_state = AppStates::DoingLocalWork;
            Some(items)
        }
        None => None,
    }
}

fn request_top_story_ids(client: &Client<HttpsConnector>,
                         endpoints: &HnNewsEndpoint)
                         -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_top_stories_path());
    create_get_request(url, &client)
}

fn request_best_stories_ids(client: &Client<HttpsConnector>,
                            endpoints: &HnNewsEndpoint)
                            -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_best_stories_path());
    create_get_request(url, &client)
}

fn request_item(item: &str,
                client: &Client<HttpsConnector>,
                endpoints: &HnNewsEndpoint)
                -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_item_path(item));
    create_get_request(url, &client)
}

pub fn download_page_from_item(item: &HnItem,
                               app_domain: &mut AppDomain,
                               state: &mut AppStateMachine)
                               -> Result<String, String> {
    match item.url {
        // todo change to async
        Some(ref url) => {
            let uri = utils::parse_url_from_str(url);
            let filename: String = utils::filename_for_hnitem(&item);
            let path = Path::new(&filename);
            let mut file: File =
                OpenOptions::new().write(true).create(true).open(path.as_os_str()).unwrap();

            let v = curl_req(url);
            let s = String::from_utf8(v).unwrap(); // TODO errors
            file.write_all(s.as_bytes());
            Ok(String::from(path.as_os_str().to_str().unwrap())) // should probably be path to string
        }
        None => Err(String::from("No url")),
    }

}

fn curl_req(url: &String) -> Vec<u8> {
    let mut vecced: Vec<u8> = Vec::new();
    let mut easy = Easy::new();
    easy.get(true).unwrap();
    easy.url(url).unwrap();
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
                vecced.extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
        transfer.perform().unwrap();
    }
    vecced
}

fn create_get_request(url: Uri, client: &Client<HttpsConnector>) -> FutureResponse {
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("rs-hackernews-cli"));
}

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

    #[test]
    fn request_item_test() {
        let mut app_domain = AppDomain::new();
        let s = String::from("8000");
        let response = app_domain.core
            .run(request_item(&s, &app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_top_story_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_best_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_best_stories_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }

    #[test]
    fn get_item_by_id_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("126809");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        assert!(hnitem.score.unwrap() != 0);
        assert!(hnitem.id != 0);
        assert!(!hnitem.type_str.is_empty());
        assert!(!hnitem.title.unwrap().is_empty());
        assert_eq!(126809, hnitem.id);
    }
    #[test]
    fn get_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let top_stories: HnListOfItems = get_top_story_ids(&mut app_domain, &mut app_sm).unwrap();
        assert!(top_stories.values.len() != 0);
    }

    #[test]
    fn get_comments_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("14114235");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        let comments: Vec<HnItem> = get_comments_for_item(&hnitem, &mut app_domain, &mut app_sm)
            .unwrap();
        assert!(comments.len() != 0);
    }
}
<|endoftext|>


<commit_before>use crate::size_hint;
use crate::Itertools;

use alloc::vec::Vec;
use std::mem::replace;
use std::fmt;

/// Head element and Tail iterator pair
///
/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on
/// first items (which are guaranteed to exist).
///
/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in
/// `KMerge` into a min-heap.
#[derive(Debug)]
struct HeadTail<I>
    where I: Iterator
{
    head: I::Item,
    tail: I,
}

impl<I> HeadTail<I>
    where I: Iterator
{
    /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
    fn new(mut it: I) -> Option<HeadTail<I>> {
        let head = it.next();
        head.map(|h| {
            HeadTail {
                head: h,
                tail: it,
            }
        })
    }

    /// Get the next element and update `head`, returning the old head in `Some`.
    ///
    /// Returns `None` when the tail is exhausted (only `head` then remains).
    fn next(&mut self) -> Option<I::Item> {
        if let Some(next) = self.tail.next() {
            Some(replace(&mut self.head, next))
        } else {
            None
        }
    }

    /// Hints at the size of the sequence, same as the `Iterator` method.
    fn size_hint(&self) -> (usize, Option<usize>) {
        size_hint::add_scalar(self.tail.size_hint(), 1)
    }
}

impl<I> Clone for HeadTail<I>
    where I: Iterator + Clone,
          I::Item: Clone
{
    clone_fields!(head, tail);
}

/// Make `data` a heap (min-heap w.r.t the sorting).
fn heapify<T, S>(data: &mut [T], mut less_than: S)
    where S: FnMut(&T, &T) -> bool
{
    for i in (0..data.len() / 2).rev() {
        sift_down(data, i, &mut less_than);
    }
}

/// Sift down element at `index` (`heap` is a min-heap wrt the ordering)
fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
    where S: FnMut(&T, &T) -> bool
{
    debug_assert!(index <= heap.len());
    let mut pos = index;
    let mut child = 2 * pos + 1;
    // the `pos` conditional is to avoid a bounds check
    while pos < heap.len() && child < heap.len() {
        let right = child + 1;

        // pick the smaller of the two children
        if right < heap.len() && less_than(&heap[right], &heap[child]) {
            child = right;
        }

        // sift down is done if we are already in order
        if !less_than(&heap[child], &heap[pos]) {
            return;
        }
        heap.swap(pos, child);
        pos = child;
        child = 2 * pos + 1;
    }
}

/// An iterator adaptor that merges an abitrary number of base iterators in ascending order.
/// If all base iterators are sorted (ascending), the result is sorted.
///
/// Iterator element type is `I::Item`.
///
/// See [`.kmerge()`](crate::Itertools::kmerge) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub type KMerge<I> = KMergeBy<I, KMergeByLt>;

pub trait KMergePredicate<T> {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool;
}

#[derive(Clone)]
pub struct KMergeByLt;

impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
        a < b
    }
}

impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
        self(a, b)
    }
}

/// Create an iterator that merges elements of the contained iterators using
/// the ordering function.
///
/// Equivalent to `iterable.into_iter().kmerge()`.
///
/// ```
/// use itertools::kmerge;
///
/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
///     /* loop body */
/// }
/// ```
pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
    where I: IntoIterator,
          I::Item: IntoIterator,
          <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
{
    kmerge_by(iterable, KMergeByLt)
}

/// An iterator adaptor that merges an abitrary number of base iterators
/// according to an ordering function.
///
/// Iterator element type is `I::Item`.
///
/// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct KMergeBy<I, F>
    where I: Iterator,
{
    heap: Vec<HeadTail<I>>,
    less_than: F,
}

impl<I, F> fmt::Debug for KMergeBy<I, F>
    where I: Iterator + fmt::Debug,
          I::Item: fmt::Debug,
{
    debug_fmt_fields!(KMergeBy, heap);
}

/// Create an iterator that merges elements of the contained iterators.
///
/// Equivalent to `iterable.into_iter().kmerge_by(less_than)`.
pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
    -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
    where I: IntoIterator,
          I::Item: IntoIterator,
          F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
{
    let iter = iterable.into_iter();
    let (lower, _) = iter.size_hint();
    let mut heap: Vec<_> = Vec::with_capacity(lower);
    heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter())));
    heapify(&mut heap, |a, b| less_than.kmerge_pred(&a.head, &b.head));
    KMergeBy { heap, less_than }
}

impl<I, F> Clone for KMergeBy<I, F>
    where I: Iterator + Clone,
          I::Item: Clone,
          F: Clone,
{
    clone_fields!(heap, less_than);
}

impl<I, F> Iterator for KMergeBy<I, F>
    where I: Iterator,
          F: KMergePredicate<I::Item>
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.heap.is_empty() {
            return None;
        }
        let result = if let Some(next) = self.heap[0].next() {
            next
        } else {
            self.heap.swap_remove(0).head
        };
        let less_than = &mut self.less_than;
        sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head));
        Some(result)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.heap.iter()
                 .map(|i| i.size_hint())
                 .fold1(size_hint::add)
                 .unwrap_or((0, Some(0)))
    }
}
<commit_msg>Remove unpredictable branch from kmerge::sift_down<commit_after>use crate::size_hint;
use crate::Itertools;

use alloc::vec::Vec;
use std::mem::replace;
use std::fmt;

/// Head element and Tail iterator pair
///
/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on
/// first items (which are guaranteed to exist).
///
/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in
/// `KMerge` into a min-heap.
#[derive(Debug)]
struct HeadTail<I>
    where I: Iterator
{
    head: I::Item,
    tail: I,
}

impl<I> HeadTail<I>
    where I: Iterator
{
    /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
    fn new(mut it: I) -> Option<HeadTail<I>> {
        let head = it.next();
        head.map(|h| {
            HeadTail {
                head: h,
                tail: it,
            }
        })
    }

    /// Get the next element and update `head`, returning the old head in `Some`.
    ///
    /// Returns `None` when the tail is exhausted (only `head` then remains).
    fn next(&mut self) -> Option<I::Item> {
        if let Some(next) = self.tail.next() {
            Some(replace(&mut self.head, next))
        } else {
            None
        }
    }

    /// Hints at the size of the sequence, same as the `Iterator` method.
    fn size_hint(&self) -> (usize, Option<usize>) {
        size_hint::add_scalar(self.tail.size_hint(), 1)
    }
}

impl<I> Clone for HeadTail<I>
    where I: Iterator + Clone,
          I::Item: Clone
{
    clone_fields!(head, tail);
}

/// Make `data` a heap (min-heap w.r.t the sorting).
fn heapify<T, S>(data: &mut [T], mut less_than: S)
    where S: FnMut(&T, &T) -> bool
{
    for i in (0..data.len() / 2).rev() {
        sift_down(data, i, &mut less_than);
    }
}

/// Sift down element at `index` (`heap` is a min-heap wrt the ordering)
fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
    where S: FnMut(&T, &T) -> bool
{
    debug_assert!(index <= heap.len());
    let mut pos = index;
    let mut child = 2 * pos + 1;
    // Require the right child to be present
    // This allows to find the index of the smallest child without a branch
    // that wouldn't be predicted if present
    while child + 1 < heap.len() {
        // pick the smaller of the two children
        // use aritmethic to avoid an unpredictable branch
        child += less_than(&heap[child+1], &heap[child]) as usize;

        // sift down is done if we are already in order
        if !less_than(&heap[child], &heap[pos]) {
            return;
        }
        heap.swap(pos, child);
        pos = child;
        child = 2 * pos + 1;
    }
    // Check if the last (left) child was an only child
    // if it is then it has to be compared with the parent
    if child + 1 == heap.len() && less_than(&heap[child], &heap[pos]) {
        heap.swap(pos, child);
    }
}

/// An iterator adaptor that merges an abitrary number of base iterators in ascending order.
/// If all base iterators are sorted (ascending), the result is sorted.
///
/// Iterator element type is `I::Item`.
///
/// See [`.kmerge()`](crate::Itertools::kmerge) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub type KMerge<I> = KMergeBy<I, KMergeByLt>;

pub trait KMergePredicate<T> {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool;
}

#[derive(Clone)]
pub struct KMergeByLt;

impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
        a < b
    }
}

impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
    fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
        self(a, b)
    }
}

/// Create an iterator that merges elements of the contained iterators using
/// the ordering function.
///
/// Equivalent to `iterable.into_iter().kmerge()`.
///
/// ```
/// use itertools::kmerge;
///
/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
///     /* loop body */
/// }
/// ```
pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
    where I: IntoIterator,
          I::Item: IntoIterator,
          <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
{
    kmerge_by(iterable, KMergeByLt)
}

/// An iterator adaptor that merges an abitrary number of base iterators
/// according to an ordering function.
///
/// Iterator element type is `I::Item`.
///
/// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct KMergeBy<I, F>
    where I: Iterator,
{
    heap: Vec<HeadTail<I>>,
    less_than: F,
}

impl<I, F> fmt::Debug for KMergeBy<I, F>
    where I: Iterator + fmt::Debug,
          I::Item: fmt::Debug,
{
    debug_fmt_fields!(KMergeBy, heap);
}

/// Create an iterator that merges elements of the contained iterators.
///
/// Equivalent to `iterable.into_iter().kmerge_by(less_than)`.
pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
    -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
    where I: IntoIterator,
          I::Item: IntoIterator,
          F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
{
    let iter = iterable.into_iter();
    let (lower, _) = iter.size_hint();
    let mut heap: Vec<_> = Vec::with_capacity(lower);
    heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter())));
    heapify(&mut heap, |a, b| less_than.kmerge_pred(&a.head, &b.head));
    KMergeBy { heap, less_than }
}

impl<I, F> Clone for KMergeBy<I, F>
    where I: Iterator + Clone,
          I::Item: Clone,
          F: Clone,
{
    clone_fields!(heap, less_than);
}

impl<I, F> Iterator for KMergeBy<I, F>
    where I: Iterator,
          F: KMergePredicate<I::Item>
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.heap.is_empty() {
            return None;
        }
        let result = if let Some(next) = self.heap[0].next() {
            next
        } else {
            self.heap.swap_remove(0).head
        };
        let less_than = &mut self.less_than;
        sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head));
        Some(result)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.heap.iter()
                 .map(|i| i.size_hint())
                 .fold1(size_hint::add)
                 .unwrap_or((0, Some(0)))
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::iter::FromIterator;

use helpers::HelperDef;
use registry::Registry;
use context::JsonRender;
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct IncludeHelper;

#[derive(Clone, Copy)]
pub struct BlockHelper;

#[derive(Clone, Copy)]
pub struct PartialHelper;

impl HelperDef for IncludeHelper {
    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let template = try!(h.params()
                                .get(0)
                                .ok_or(RenderError::new("Param not found for helper"))
                                .and_then(|ref t| {
            t.path()
                .or(Some(&t.value().render()))
                .ok_or(RenderError::new("Invalid template name to include"))
                .and_then(|p| if rc.is_current_template(p) {
                              Err(RenderError::new("Cannot include self in >"))
                          } else {
                              Ok(r.get_template(&p))
                          })
        }));

        let context_param = h.params().get(1).and_then(|p| p.path());
        let old_path = match context_param {
            Some(p) => {
                let old_path = rc.get_path().clone();
                rc.promote_local_vars();
                let new_path = format!("{}/{}", old_path, p);
                rc.set_path(new_path);
                Some(old_path)
            }
            None => None,
        };

        let result = match template {
            Some(t) => {
                if h.hash().is_empty() {
                    t.render(r, rc)
                } else {
                    let hash_ctx = BTreeMap::from_iter(h.hash().iter().map(|(k, v)| {
                                                                               (k.clone(),
                                                                                v.value().clone())
                                                                           }));
                    let mut local_rc = rc.derive();

                    {
                        let mut ctx_ref = local_rc.context_mut();
                        *ctx_ref = ctx_ref.extend(&hash_ctx);
                    }
                    t.render(r, &mut local_rc)
                }
            }
            None => Err(RenderError::new("Template not found.")),
        };

        if let Some(path) = old_path {
            rc.set_path(path);
            rc.demote_local_vars();
        }

        result
    }
}

impl HelperDef for BlockHelper {
    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = try!(h.param(0).ok_or_else(|| RenderError::new("Param not found for helper")));

        if let Some(partial_path) = param.path() {
            let partial_template = rc.get_partial(partial_path);

            match partial_template {
                Some(partial_template) => partial_template.render(r, rc),
                None => h.template().unwrap().render(r, rc),
            }
        } else {
            Err(RenderError::new("Do not use literal here, use template name directly."))
        }
    }
}

impl HelperDef for PartialHelper {
    fn call(&self, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = try!(h.param(0).ok_or_else(|| RenderError::new("Param not found for helper")));

        if let Some(partial_path) = param.path() {
            rc.set_partial(partial_path.to_owned(), h.template().unwrap().clone());
        }

        Ok(())
    }
}

pub static INCLUDE_HELPER: IncludeHelper = IncludeHelper;
pub static BLOCK_HELPER: BlockHelper = BlockHelper;
pub static PARTIAL_HELPER: PartialHelper = PartialHelper;

#[cfg(test)]
mod test {
    use registry::Registry;
    use std::collections::BTreeMap;

    #[test]
    fn test() {
        let t0 = "
{{#block title}}default{{/block}}
";
        let t1 = "{{#partial title}}{{this}}{{/partial}}{{> t0}}";
        let t2 = "{{> t0}}
{{this}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());
        assert!(handlebars.register_template_string("t2", t2).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "
true
".to_string());

        let r1 = handlebars.render("t2", &true);
        assert_eq!(r1.ok().unwrap(), "
default

true
".to_string());
    }

    #[test]
    fn test_context() {
        let t0 = "
{{> (body) data}}
";
        let t1 = "
{{this}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("body".into(), "t1".into());
        map.insert("data".into(), "hello".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(), "

hello

".to_string());
    }

    #[test]
    fn test_partial_hash_context() {
        let t0 = "
{{> t1 hello=\"world\"}}
";
        let t1 = "
{{data}}

{{hello}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("data".into(), "hello".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(),
                   "

hello

world

".to_string());
    }

    #[test]
    fn test_inline_partial() {
        let t0 = "{{#partial title}}hello {{name}}{{/partial}}
include partial: {{#block title}}{{/block}}
";
        let t1 = "{{#block none_partial}}Partial not found{{/block}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("name".into(), "world".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(),
                   "
include partial: hello world
".to_string());

        let r1 = handlebars.render("t1", &map);
        assert_eq!(r1.ok().unwrap(), "Partial not found".to_string());
    }

    #[test]
    fn test_include_self() {
        let t0 = "
{{> t0}}
";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());

        let map: BTreeMap<String, String> = BTreeMap::new();

        let r0 = handlebars.render("t0", &map);
        assert!(r0.is_err());
    }

}
<commit_msg>(fix) import error for partial_legacy<commit_after>use std::collections::BTreeMap;
use std::iter::FromIterator;

use helpers::HelperDef;
use registry::Registry;
use context::JsonRender;
use render::{Renderable, RenderContext, Helper};
use error::RenderError;

#[derive(Clone, Copy)]
pub struct IncludeHelper;

#[derive(Clone, Copy)]
pub struct BlockHelper;

#[derive(Clone, Copy)]
pub struct PartialHelper;

impl HelperDef for IncludeHelper {
    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let template = try!(h.params()
                                .get(0)
                                .ok_or(RenderError::new("Param not found for helper"))
                                .and_then(|ref t| {
            t.path()
                .or(Some(&t.value().render()))
                .ok_or(RenderError::new("Invalid template name to include"))
                .and_then(|p| if rc.is_current_template(p) {
                              Err(RenderError::new("Cannot include self in >"))
                          } else {
                              Ok(r.get_template(&p))
                          })
        }));

        let context_param = h.params().get(1).and_then(|p| p.path());
        let old_path = match context_param {
            Some(p) => {
                let old_path = rc.get_path().clone();
                rc.promote_local_vars();
                let new_path = format!("{}/{}", old_path, p);
                rc.set_path(new_path);
                Some(old_path)
            }
            None => None,
        };

        let result = match template {
            Some(t) => {
                if h.hash().is_empty() {
                    t.render(r, rc)
                } else {
                    let hash_ctx = BTreeMap::from_iter(h.hash().iter().map(|(k, v)| {
                                                                               (k.clone(),
                                                                                v.value().clone())
                                                                           }));
                    let mut local_rc = rc.derive();

                    {
                        let mut ctx_ref = local_rc.context_mut();
                        *ctx_ref = ctx_ref.extend(&hash_ctx);
                    }
                    t.render(r, &mut local_rc)
                }
            }
            None => Err(RenderError::new("Template not found.")),
        };

        if let Some(path) = old_path {
            rc.set_path(path);
            rc.demote_local_vars();
        }

        result
    }
}

impl HelperDef for BlockHelper {
    fn call(&self, h: &Helper, r: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = try!(h.param(0).ok_or_else(|| RenderError::new("Param not found for helper")));

        if let Some(partial_path) = param.path() {
            let partial_template = rc.get_partial(partial_path);

            match partial_template {
                Some(partial_template) => partial_template.render(r, rc),
                None => h.template().unwrap().render(r, rc),
            }
        } else {
            Err(RenderError::new("Do not use literal here, use template name directly."))
        }
    }
}

impl HelperDef for PartialHelper {
    fn call(&self, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<(), RenderError> {
        let param = try!(h.param(0).ok_or_else(|| RenderError::new("Param not found for helper")));

        if let Some(partial_path) = param.path() {
            rc.set_partial(partial_path.to_owned(), h.template().unwrap().clone());
        }

        Ok(())
    }
}

pub static INCLUDE_HELPER: IncludeHelper = IncludeHelper;
pub static BLOCK_HELPER: BlockHelper = BlockHelper;
pub static PARTIAL_HELPER: PartialHelper = PartialHelper;

#[cfg(test)]
mod test {
    use registry::Registry;
    use std::collections::BTreeMap;

    #[test]
    fn test() {
        let t0 = "
{{#block title}}default{{/block}}
";
        let t1 = "{{#partial title}}{{this}}{{/partial}}{{> t0}}";
        let t2 = "{{> t0}}
{{this}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());
        assert!(handlebars.register_template_string("t2", t2).is_ok());

        let r0 = handlebars.render("t1", &true);
        assert_eq!(r0.ok().unwrap(), "
true
".to_string());

        let r1 = handlebars.render("t2", &true);
        assert_eq!(r1.ok().unwrap(), "
default

true
".to_string());
    }

    #[test]
    fn test_context() {
        let t0 = "
{{> (body) data}}
";
        let t1 = "
{{this}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("body".into(), "t1".into());
        map.insert("data".into(), "hello".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(), "

hello

".to_string());
    }

    #[test]
    fn test_partial_hash_context() {
        let t0 = "
{{> t1 hello=\"world\"}}
";
        let t1 = "
{{data}}

{{hello}}
";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("data".into(), "hello".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(),
                   "

hello

world

".to_string());
    }

    #[test]
    fn test_inline_partial() {
        let t0 = "{{#partial title}}hello {{name}}{{/partial}}
include partial: {{#block title}}{{/block}}
";
        let t1 = "{{#block none_partial}}Partial not found{{/block}}";

        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());
        assert!(handlebars.register_template_string("t1", t1).is_ok());

        let mut map: BTreeMap<String, String> = BTreeMap::new();
        map.insert("name".into(), "world".into());

        let r0 = handlebars.render("t0", &map);
        assert_eq!(r0.ok().unwrap(),
                   "
include partial: hello world
".to_string());

        let r1 = handlebars.render("t1", &map);
        assert_eq!(r1.ok().unwrap(), "Partial not found".to_string());
    }

    #[test]
    fn test_include_self() {
        let t0 = "
{{> t0}}
";
        let mut handlebars = Registry::new();
        assert!(handlebars.register_template_string("t0", t0).is_ok());

        let map: BTreeMap<String, String> = BTreeMap::new();

        let r0 = handlebars.render("t0", &map);
        assert!(r0.is_err());
    }

}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]
use data::timeinfo::*;
use data::pixel::*;
use data::audiosample::*;

use alloc::heap::{Heap, Alloc, Layout};
use bytes::Bytes;
// use std::ptr::write_bytes;

error_chain! {
    errors {
        InvalidIndex
    }
}

pub struct VideoFrame {
    pub width: usize,
    pub height: usize,
    pub format: Box<Formaton>,
}

impl VideoFrame {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

pub struct AudioFrame {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Box<Soniton>,
}

impl AudioFrame {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

pub enum FrameKind {
    Video(VideoFrame),
    Audio(AudioFrame),
}

use self::FrameKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> usize;
    fn count(&self) -> usize;
}

pub struct Frame {
    kind: FrameKind,
    buf: Box<FrameBuffer>,
    t: TimeInfo,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: Bytes,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: Bytes,
    planes: Vec<Plane>,
}

impl DefaultFrameBuffer {
    pub fn new(kind: &FrameKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe { Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap()).unwrap() };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = Bytes::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.into_iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}
<commit_msg>frame: rename VideoFrame, AudioFrame<commit_after>#![allow(dead_code, unused_variables)]
use data::timeinfo::*;
use data::pixel::*;
use data::audiosample::*;

use alloc::heap::{Heap, Alloc, Layout};
use bytes::Bytes;
// use std::ptr::write_bytes;

error_chain! {
    errors {
        InvalidIndex
    }
}

pub struct VideoInfo {
    pub width: usize,
    pub height: usize,
    pub format: Box<Formaton>,
}

impl VideoInfo {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

pub struct AudioInfo {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Box<Soniton>,
}

impl AudioInfo {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

pub enum MediaKind {
    Video(VideoInfo),
    Audio(AudioInfo),
}

use self::MediaKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> usize;
    fn count(&self) -> usize;
}

pub struct Frame {
    kind: MediaKind,
    buf: Box<FrameBuffer>,
    t: TimeInfo,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: Bytes,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: Bytes,
    planes: Vec<Plane>,
}

impl DefaultFrameBuffer {
    pub fn new(kind: &MediaKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe { Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap()).unwrap() };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = Bytes::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.into_iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}
<|endoftext|>