<commit_before>use crate::{
    buffer::Buffer,
    channels::Channels,
    connection_status::ConnectionState,
    frames::Frames,
    heartbeat::Heartbeat,
    internal_rpc::InternalRPC,
    protocol::{self, AMQPError, AMQPHardError},
    reactor::{ReactorBuilder, ReactorHandle, Slot},
    socket_state::SocketState,
    tcp::HandshakeResult,
    thread::ThreadHandle,
    Configuration, ConnectionStatus, Error, PromiseResolver, Result, TcpStream,
};
use amq_protocol::frame::{gen_frame, parse_frame, AMQPFrame, GenError};
use log::{debug, error, trace};
use std::{
    collections::VecDeque,
    convert::TryFrom,
    io::{self, Write},
    sync::Arc,
    thread::Builder as ThreadBuilder,
    time::Duration,
};

const FRAMES_STORAGE: usize = 32;

#[derive(Debug, PartialEq)]
enum Status {
    Initial,
    SocketConnected,
    SocketWritable,
    Connected,
    Stop,
}

pub struct IoLoop {
    connection_status: ConnectionStatus,
    configuration: Configuration,
    channels: Channels,
    internal_rpc: InternalRPC,
    frames: Frames,
    heartbeat: Heartbeat,
    socket_state: SocketState,
    reactor: Box<dyn ReactorHandle + Send>,
    reactor_thread_handle: ThreadHandle,
    connection_io_loop_handle: ThreadHandle,
    stream: TcpStream,
    slot: Slot,
    status: Status,
    frame_size: usize,
    receive_buffer: Buffer,
    send_buffer: Buffer,
    serialized_frames: VecDeque<(u64, Option<PromiseResolver<()>>)>,
}

impl IoLoop {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new(
        connection_status: ConnectionStatus,
        configuration: Configuration,
        channels: Channels,
        internal_rpc: InternalRPC,
        frames: Frames,
        socket_state: SocketState,
        connection_io_loop_handle: ThreadHandle,
        stream: HandshakeResult,
        reactor_builder: &dyn ReactorBuilder,
    ) -> Result<Self> {
        let mut stream = TcpStream::try_from(stream)?;
        let heartbeat = Heartbeat::new(channels.clone());
        let mut reactor = reactor_builder.build(heartbeat.clone())?;
        let reactor_handle = reactor.handle();
        let frame_size = std::cmp::max(
            protocol::constants::FRAME_MIN_SIZE as usize,
            configuration.frame_max() as usize,
        );
        let slot = reactor.register(stream.inner_mut(), socket_state.handle())?;

        let reactor_thread_handle = reactor.start()?;

        Ok(Self {
            connection_status,
            configuration,
            channels,
            internal_rpc,
            frames,
            heartbeat,
            socket_state,
            reactor: reactor_handle,
            reactor_thread_handle,
            connection_io_loop_handle,
            stream,
            slot,
            status: Status::Initial,
            frame_size,
            receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            send_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            serialized_frames: VecDeque::default(),
        })
    }

    fn ensure_connected(&mut self) -> Result<bool> {
        match self.stream.inner().peer_addr() {
            Ok(peer) => {
                debug!("Connecting to {}", peer);
                self.status = Status::SocketConnected;
                Ok(true)
            }
            Err(err) => {
                if let io::ErrorKind::NotConnected = err.kind() {
                    Ok(false)
                } else {
                    Err(err.into())
                }
            }
        }
    }

    fn ensure_writable(&mut self) -> Result<bool> {
        match self.stream.inner().is_writable() {
            Ok(()) => {
                self.status = Status::SocketWritable;
                Ok(true)
            }
            Err(err) => {
                if let io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock = err.kind() {
                    Ok(false)
                } else {
                    Err(err.into())
                }
            }
        }
    }

    fn finish_setup(&mut self) -> Result<bool> {
        if self.connection_status.connected() {
            let frame_max = self.configuration.frame_max() as usize;
            self.frame_size = std::cmp::max(self.frame_size, frame_max);
            self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
            self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
            let heartbeat = self.configuration.heartbeat();
            if heartbeat != 0 {
                let heartbeat = Duration::from_millis(u64::from(heartbeat) * 500); // * 1000 (ms) / 2 (half the negotiated timeout)
                self.heartbeat.set_timeout(heartbeat);
                self.reactor.start_heartbeat();
            }
            self.status = Status::Connected;
        }
        Ok(true)
    }

    fn ensure_setup(&mut self) -> Result<bool> {
        loop {
            let success = match self.status {
                Status::Initial => self.ensure_connected(),
                Status::SocketConnected => self.ensure_writable(),
                Status::SocketWritable => return self.finish_setup(),
                Status::Connected => return Ok(true),
                Status::Stop => return Ok(false),
            }?;
            if !success {
                return Ok(false);
            }
        }
    }

    fn has_data(&self) -> bool {
        self.frames.has_pending()
            || self.send_buffer.available_data() > 0
            || !self.serialized_frames.is_empty()
    }

    fn can_write(&mut self) -> bool {
        self.socket_state.writable() && self.has_data() && !self.connection_status.blocked()
    }

    fn can_read(&mut self) -> bool {
        self.socket_state.readable() && self.receive_buffer.available_space() > 0
    }

    fn can_parse(&self) -> bool {
        self.receive_buffer.available_data() > 0
    }

    fn should_continue(&self) -> bool {
        (self.status != Status::Connected
            || self.connection_status.connected()
            || self.connection_status.closing())
            && self.status != Status::Stop
            && !self.connection_status.errored()
    }

    pub fn start(mut self) -> Result<()> {
        let waker = self.socket_state.handle();
        self.connection_io_loop_handle.clone().register(
            ThreadBuilder::new()
                .name("lapin-io-loop".to_owned())
                .spawn(move || {
                    while self.should_continue() {
                        if let Err(err) = self.run() {
                            self.critical_error(err)?;
                        }
                    }
                    self.heartbeat.cancel();
                    self.reactor.shutdown()?;
                    self.reactor_thread_handle.wait("reactor")
                })?,
        );
        waker.wake();
        Ok(())
    }

    fn poll_internal_rpc(&self) -> Result<()> {
        self.internal_rpc.poll(&self.channels)
    }

    fn poll_socket_events(&mut self) -> Result<()> {
        self.socket_state.poll_events();
        if self.socket_state.error() {
            self.critical_error(io::Error::from(io::ErrorKind::ConnectionAborted).into())?;
        }
        if self.socket_state.closed() {
            self.critical_error(io::Error::from(io::ErrorKind::ConnectionReset).into())?;
        }
        self.poll_internal_rpc()
    }

    fn run(&mut self) -> Result<()> {
        trace!("io_loop run");
        self.poll_socket_events()?;
        if !self.ensure_setup()? {
            return Ok(());
        }
        trace!(
            "io_loop do_run; can_read={}, can_write={}, has_data={}",
            self.socket_state.readable(),
            self.socket_state.writable(),
            self.has_data()
        );
        if !self.can_read() && !self.can_write() {
            self.socket_state.wait();
        }
        self.poll_socket_events()?;
        if self.stream.is_handshaking() {
            self.stream.handshake()?;
            if self.stream.is_handshaking() {
                // We hit WOULDBLOCK while handshaking, wait for the next socket event
                return Ok(());
            }
        }
        self.write()?;
        if self.connection_status.closed() {
            self.status = Status::Stop;
        }
        if self.should_continue() {
            self.read()?;
        }
        self.handle_frames()?;
        trace!(
            "io_loop do_run done; can_read={}, can_write={}, has_data={}, status={:?}",
            self.socket_state.readable(),
            self.socket_state.writable(),
            self.has_data(),
            self.status
        );
        self.poll_internal_rpc()
    }

    fn critical_error(&mut self, error: Error) -> Result<()> {
        if let Some(resolver) = self.connection_status.connection_resolver() {
            resolver.swear(Err(error.clone()));
        }
        self.status = Status::Stop;
        self.channels.set_connection_error(error.clone())?;
        for (_, resolver) in std::mem::take(&mut self.serialized_frames) {
            if let Some(resolver) = resolver {
                resolver.swear(Err(error.clone()));
            }
        }
        self.reactor.shutdown()?;
        Err(error)
    }

    fn handle_read_result(&mut self, result: Result<()>) -> Result<()> {
        if let Err(e) = self
            .socket_state
            .handle_read_result(result, &*self.reactor, self.slot)
        {
            error!("error reading: {:?}", e);
            self.critical_error(e)?;
        }
        self.poll_internal_rpc()
    }

    fn handle_write_result(&mut self, result: Result<()>) -> Result<()> {
        if let Err(e) = self
            .socket_state
            .handle_write_result(result, &*self.reactor, self.slot)
        {
            error!("error writing: {:?}", e);
            self.critical_error(e)?;
        }
        self.poll_internal_rpc()
    }

    fn flush(&mut self) -> Result<()> {
        self.stream.flush()?;
        self.poll_internal_rpc()
    }

    fn write(&mut self) -> Result<()> {
        if self.socket_state.writable() {
            let res = self.flush();
            self.handle_write_result(res)?;
        }
        while self.can_write() {
            let res = self.write_to_stream();
            self.handle_write_result(res)?;
        }
        self.poll_internal_rpc()
    }

    fn read(&mut self) -> Result<()> {
        while self.can_read() {
            let res = self.read_from_stream();
            self.handle_read_result(res)?;
        }
        self.poll_internal_rpc()
    }

    fn write_to_stream(&mut self) -> Result<()> {
        self.flush()?;
        self.serialize()?;

        let sz = self.send_buffer.write_to(&mut self.stream)?;

        if sz > 0 {
            self.heartbeat.update_last_write();

            trace!("wrote {} bytes", sz);
            self.send_buffer.consume(sz);

            let mut written = sz as u64;
            while written > 0 {
                if let Some((to_write, resolver)) = self.serialized_frames.pop_front() {
                    if written < to_write {
                        self.serialized_frames
                            .push_front((to_write - written, resolver));
                        trace!("{} to write to complete this frame", to_write - written);
                        written = 0;
                    } else {
                        if let Some(resolver) = resolver {
                            resolver.swear(Ok(()));
                        }
                        written -= to_write;
                    }
                } else {
                    error!(
                        "We've written {} but didn't expect to write anything",
                        written
                    );
                    break;
                }
            }

            if self.send_buffer.available_data() > 0 {
                // We didn't write all the data yet
                trace!("Still {} to write", self.send_buffer.available_data());
            }

            self.flush()?;
        } else {
            error!("Socket was writable but we wrote 0, marking as wouldblock");
            self.handle_write_result(Err(io::Error::from(io::ErrorKind::WouldBlock).into()))?;
        }
        self.poll_internal_rpc()
    }

    fn read_from_stream(&mut self) -> Result<()> {
        match self.connection_status.state() {
            ConnectionState::Closed => Ok(()),
            ConnectionState::Error => Err(Error::InvalidConnectionState(ConnectionState::Error)),
            _ => {
                let sz = self.receive_buffer.read_from(&mut self.stream)?;

                if sz > 0 {
                    trace!("read {} bytes", sz);
                    self.receive_buffer.fill(sz);
                } else {
                    error!("Socket was readable but we read 0, marking as wouldblock");
                    self.handle_read_result(
                        Err(io::Error::from(io::ErrorKind::WouldBlock).into()),
                    )?;
                }
                self.poll_internal_rpc()
            }
        }
    }

    fn serialize(&mut self) -> Result<()> {
        while let Some((next_msg, resolver)) = self.frames.pop(self.channels.flow()) {
            trace!("will write to buffer: {}", next_msg);
            let checkpoint = self.send_buffer.checkpoint();
            let res = gen_frame(&next_msg)((&mut self.send_buffer).into());
            match res.map(|w| w.into_inner().1) {
                Ok(sz) => self.serialized_frames.push_back((sz, resolver)),
                Err(e) => {
                    self.send_buffer.rollback(checkpoint);
                    match e {
                        GenError::BufferTooSmall(_) => {
                            // Requeue msg
                            self.frames.retry((next_msg, resolver));
                            break;
                        }
                        e => {
                            error!("error generating frame: {:?}", e);
                            self.critical_error(Error::SerialisationError(Arc::new(e)))?;
                        }
                    }
                }
            }
        }
        self.poll_internal_rpc()
    }

    fn handle_frames(&mut self) -> Result<()> {
        while self.can_parse() {
            if let Some(frame) = self.parse()? {
                self.channels.handle_frame(frame)?;
            } else {
                break;
            }
        }
        self.poll_internal_rpc()
    }

    fn parse(&mut self) -> Result<Option<AMQPFrame>> {
        match parse_frame(self.receive_buffer.parsing_context()) {
            Ok((i, f)) => {
                let consumed = self.receive_buffer.offset(i);
                let frame_max = self.configuration.frame_max() as usize;
                if frame_max > 0 && consumed > frame_max {
                    error!("received large ({} bytes) frame", consumed);
                    let error = AMQPError::new(
                        AMQPHardError::FRAMEERROR.into(),
                        format!("frame too large: {} bytes", consumed).into(),
                    );
                    self.internal_rpc.handle().close_connection(
                        error.get_id(),
                        error.get_message().to_string(),
                        0,
                        0,
                    );
                    self.poll_internal_rpc()?;
                    self.critical_error(Error::ProtocolError(error))?;
                }
                self.receive_buffer.consume(consumed);
                Ok(Some(f))
            }
            Err(e) => {
                if !e.is_incomplete() {
                    error!("parse error: {:?}", e);
                    self.critical_error(Error::ParsingError(e))?;
                }
                Ok(None)
            }
        }
    }
}
<commit_msg>IoLoop: simplify ensure_setup<commit_after>use crate::{
    buffer::Buffer,
    channels::Channels,
    connection_status::ConnectionState,
    frames::Frames,
    heartbeat::Heartbeat,
    internal_rpc::InternalRPC,
    protocol::{self, AMQPError, AMQPHardError},
    reactor::{ReactorBuilder, ReactorHandle, Slot},
    socket_state::SocketState,
    tcp::HandshakeResult,
    thread::ThreadHandle,
    Configuration, ConnectionStatus, Error, PromiseResolver, Result, TcpStream,
};
use amq_protocol::frame::{gen_frame, parse_frame, AMQPFrame, GenError};
use log::{debug, error, trace};
use std::{
    collections::VecDeque,
    convert::TryFrom,
    io::{self, Write},
    sync::Arc,
    thread::Builder as ThreadBuilder,
    time::Duration,
};

const FRAMES_STORAGE: usize = 32;

#[derive(Debug, PartialEq)]
enum Status {
    Initial,
    Connected,
    Stop,
}

pub struct IoLoop {
    connection_status: ConnectionStatus,
    configuration: Configuration,
    channels: Channels,
    internal_rpc: InternalRPC,
    frames: Frames,
    heartbeat: Heartbeat,
    socket_state: SocketState,
    reactor: Box<dyn ReactorHandle + Send>,
    reactor_thread_handle: ThreadHandle,
    connection_io_loop_handle: ThreadHandle,
    stream: TcpStream,
    slot: Slot,
    status: Status,
    frame_size: usize,
    receive_buffer: Buffer,
    send_buffer: Buffer,
    serialized_frames: VecDeque<(u64, Option<PromiseResolver<()>>)>,
}

impl IoLoop {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new(
        connection_status: ConnectionStatus,
        configuration: Configuration,
        channels: Channels,
        internal_rpc: InternalRPC,
        frames: Frames,
        socket_state: SocketState,
        connection_io_loop_handle: ThreadHandle,
        stream: HandshakeResult,
        reactor_builder: &dyn ReactorBuilder,
    ) -> Result<Self> {
        let mut stream = TcpStream::try_from(stream)?;
        let heartbeat = Heartbeat::new(channels.clone());
        let mut reactor = reactor_builder.build(heartbeat.clone())?;
        let reactor_handle = reactor.handle();
        let frame_size = std::cmp::max(
            protocol::constants::FRAME_MIN_SIZE as usize,
            configuration.frame_max() as usize,
        );
        let slot = reactor.register(stream.inner_mut(), socket_state.handle())?;

        let reactor_thread_handle = reactor.start()?;

        Ok(Self {
            connection_status,
            configuration,
            channels,
            internal_rpc,
            frames,
            heartbeat,
            socket_state,
            reactor: reactor_handle,
            reactor_thread_handle,
            connection_io_loop_handle,
            stream,
            slot,
            status: Status::Initial,
            frame_size,
            receive_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            send_buffer: Buffer::with_capacity(FRAMES_STORAGE * frame_size),
            serialized_frames: VecDeque::default(),
        })
    }

    fn finish_setup(&mut self) -> Result<bool> {
        if self.connection_status.connected() {
            let frame_max = self.configuration.frame_max() as usize;
            self.frame_size = std::cmp::max(self.frame_size, frame_max);
            self.receive_buffer.grow(FRAMES_STORAGE * self.frame_size);
            self.send_buffer.grow(FRAMES_STORAGE * self.frame_size);
            let heartbeat = self.configuration.heartbeat();
            if heartbeat != 0 {
                let heartbeat = Duration::from_millis(u64::from(heartbeat) * 500); // * 1000 (ms) / 2 (half the negotiated timeout)
                self.heartbeat.set_timeout(heartbeat);
                self.reactor.start_heartbeat();
            }
            let peer = self.stream.inner().peer_addr()?;
            debug!("Connected to {}", peer);
            self.status = Status::Connected;
        }
        Ok(true)
    }

    fn ensure_setup(&mut self) -> Result<bool> {
        match self.status {
            Status::Initial => self.finish_setup(),
            Status::Connected => Ok(true),
            Status::Stop => Ok(false),
        }
    }

    fn has_data(&self) -> bool {
        self.frames.has_pending()
            || self.send_buffer.available_data() > 0
            || !self.serialized_frames.is_empty()
    }

    fn can_write(&mut self) -> bool {
        self.socket_state.writable() && self.has_data() && !self.connection_status.blocked()
    }

    fn can_read(&mut self) -> bool {
        self.socket_state.readable() && self.receive_buffer.available_space() > 0
    }

    fn can_parse(&self) -> bool {
        self.receive_buffer.available_data() > 0
    }

    fn should_continue(&self) -> bool {
        (self.status != Status::Connected
            || self.connection_status.connected()
            || self.connection_status.closing())
            && self.status != Status::Stop
            && !self.connection_status.errored()
    }

    pub fn start(mut self) -> Result<()> {
        let waker = self.socket_state.handle();
        self.connection_io_loop_handle.clone().register(
            ThreadBuilder::new()
                .name("lapin-io-loop".to_owned())
                .spawn(move || {
                    while self.should_continue() {
                        if let Err(err) = self.run() {
                            self.critical_error(err)?;
                        }
                    }
                    self.heartbeat.cancel();
                    self.reactor.shutdown()?;
                    self.reactor_thread_handle.wait("reactor")
                })?,
        );
        waker.wake();
        Ok(())
    }

    fn poll_internal_rpc(&self) -> Result<()> {
        self.internal_rpc.poll(&self.channels)
    }

    fn poll_socket_events(&mut self) -> Result<()> {
        self.socket_state.poll_events();
        if self.socket_state.error() {
            self.critical_error(io::Error::from(io::ErrorKind::ConnectionAborted).into())?;
        }
        if self.socket_state.closed() {
            self.critical_error(io::Error::from(io::ErrorKind::ConnectionReset).into())?;
        }
        self.poll_internal_rpc()
    }

    fn run(&mut self) -> Result<()> {
        trace!("io_loop run");
        self.poll_socket_events()?;
        if !self.ensure_setup()? {
            return Ok(());
        }
        trace!(
            "io_loop do_run; can_read={}, can_write={}, has_data={}",
            self.socket_state.readable(),
            self.socket_state.writable(),
            self.has_data()
        );
        if !self.can_read() && !self.can_write() {
            self.socket_state.wait();
        }
        self.poll_socket_events()?;
        if self.stream.is_handshaking() {
            self.stream.handshake()?;
            if self.stream.is_handshaking() {
                // We hit WOULDBLOCK while handshaking, wait for the next socket event
                return Ok(());
            }
        }
        self.write()?;
        if self.connection_status.closed() {
            self.status = Status::Stop;
        }
        if self.should_continue() {
            self.read()?;
        }
        self.handle_frames()?;
        trace!(
            "io_loop do_run done; can_read={}, can_write={}, has_data={}, status={:?}",
            self.socket_state.readable(),
            self.socket_state.writable(),
            self.has_data(),
            self.status
        );
        self.poll_internal_rpc()
    }

    fn critical_error(&mut self, error: Error) -> Result<()> {
        if let Some(resolver) = self.connection_status.connection_resolver() {
            resolver.swear(Err(error.clone()));
        }
        self.status = Status::Stop;
        self.channels.set_connection_error(error.clone())?;
        for (_, resolver) in std::mem::take(&mut self.serialized_frames) {
            if let Some(resolver) = resolver {
                resolver.swear(Err(error.clone()));
            }
        }
        self.reactor.shutdown()?;
        Err(error)
    }

    fn handle_read_result(&mut self, result: Result<()>) -> Result<()> {
        if let Err(e) = self
            .socket_state
            .handle_read_result(result, &*self.reactor, self.slot)
        {
            error!("error reading: {:?}", e);
            self.critical_error(e)?;
        }
        self.poll_internal_rpc()
    }

    fn handle_write_result(&mut self, result: Result<()>) -> Result<()> {
        if let Err(e) = self
            .socket_state
            .handle_write_result(result, &*self.reactor, self.slot)
        {
            error!("error writing: {:?}", e);
            self.critical_error(e)?;
        }
        self.poll_internal_rpc()
    }

    fn flush(&mut self) -> Result<()> {
        self.stream.flush()?;
        self.poll_internal_rpc()
    }

    fn write(&mut self) -> Result<()> {
        if self.socket_state.writable() {
            let res = self.flush();
            self.handle_write_result(res)?;
        }
        while self.can_write() {
            let res = self.write_to_stream();
            self.handle_write_result(res)?;
        }
        self.poll_internal_rpc()
    }

    fn read(&mut self) -> Result<()> {
        while self.can_read() {
            let res = self.read_from_stream();
            self.handle_read_result(res)?;
        }
        self.poll_internal_rpc()
    }

    fn write_to_stream(&mut self) -> Result<()> {
        self.flush()?;
        self.serialize()?;

        let sz = self.send_buffer.write_to(&mut self.stream)?;

        if sz > 0 {
            self.heartbeat.update_last_write();

            trace!("wrote {} bytes", sz);
            self.send_buffer.consume(sz);

            let mut written = sz as u64;
            while written > 0 {
                if let Some((to_write, resolver)) = self.serialized_frames.pop_front() {
                    if written < to_write {
                        self.serialized_frames
                            .push_front((to_write - written, resolver));
                        trace!("{} to write to complete this frame", to_write - written);
                        written = 0;
                    } else {
                        if let Some(resolver) = resolver {
                            resolver.swear(Ok(()));
                        }
                        written -= to_write;
                    }
                } else {
                    error!(
                        "We've written {} but didn't expect to write anything",
                        written
                    );
                    break;
                }
            }

            if self.send_buffer.available_data() > 0 {
                // We didn't write all the data yet
                trace!("Still {} to write", self.send_buffer.available_data());
            }

            self.flush()?;
        } else {
            error!("Socket was writable but we wrote 0, marking as wouldblock");
            self.handle_write_result(Err(io::Error::from(io::ErrorKind::WouldBlock).into()))?;
        }
        self.poll_internal_rpc()
    }

    fn read_from_stream(&mut self) -> Result<()> {
        match self.connection_status.state() {
            ConnectionState::Closed => Ok(()),
            ConnectionState::Error => Err(Error::InvalidConnectionState(ConnectionState::Error)),
            _ => {
                let sz = self.receive_buffer.read_from(&mut self.stream)?;

                if sz > 0 {
                    trace!("read {} bytes", sz);
                    self.receive_buffer.fill(sz);
                } else {
                    error!("Socket was readable but we read 0, marking as wouldblock");
                    self.handle_read_result(
                        Err(io::Error::from(io::ErrorKind::WouldBlock).into()),
                    )?;
                }
                self.poll_internal_rpc()
            }
        }
    }

    fn serialize(&mut self) -> Result<()> {
        while let Some((next_msg, resolver)) = self.frames.pop(self.channels.flow()) {
            trace!("will write to buffer: {}", next_msg);
            let checkpoint = self.send_buffer.checkpoint();
            let res = gen_frame(&next_msg)((&mut self.send_buffer).into());
            match res.map(|w| w.into_inner().1) {
                Ok(sz) => self.serialized_frames.push_back((sz, resolver)),
                Err(e) => {
                    self.send_buffer.rollback(checkpoint);
                    match e {
                        GenError::BufferTooSmall(_) => {
                            // Requeue msg
                            self.frames.retry((next_msg, resolver));
                            break;
                        }
                        e => {
                            error!("error generating frame: {:?}", e);
                            self.critical_error(Error::SerialisationError(Arc::new(e)))?;
                        }
                    }
                }
            }
        }
        self.poll_internal_rpc()
    }

    fn handle_frames(&mut self) -> Result<()> {
        while self.can_parse() {
            if let Some(frame) = self.parse()? {
                self.channels.handle_frame(frame)?;
            } else {
                break;
            }
        }
        self.poll_internal_rpc()
    }

    fn parse(&mut self) -> Result<Option<AMQPFrame>> {
        match parse_frame(self.receive_buffer.parsing_context()) {
            Ok((i, f)) => {
                let consumed = self.receive_buffer.offset(i);
                let frame_max = self.configuration.frame_max() as usize;
                if frame_max > 0 && consumed > frame_max {
                    error!("received large ({} bytes) frame", consumed);
                    let error = AMQPError::new(
                        AMQPHardError::FRAMEERROR.into(),
                        format!("frame too large: {} bytes", consumed).into(),
                    );
                    self.internal_rpc.handle().close_connection(
                        error.get_id(),
                        error.get_message().to_string(),
                        0,
                        0,
                    );
                    self.poll_internal_rpc()?;
                    self.critical_error(Error::ProtocolError(error))?;
                }
                self.receive_buffer.consume(consumed);
                Ok(Some(f))
            }
            Err(e) => {
                if !e.is_incomplete() {
                    error!("parse error: {:?}", e);
                    self.critical_error(Error::ParsingError(e))?;
                }
                Ok(None)
            }
        }
    }
}
<|endoftext|>


<commit_before>use {VRService, VRServicePtr, VRDevice, VRDevicePtr, VRDisplayEvent, VRDisplayEventReason};
use super::constants;
use super::device::{OpenVRDevice, OpenVRDevicePtr};
use super::openvr_sys as openvr;
use super::openvr_sys::EVRInitError::*;
use super::openvr_sys::EVRApplicationType::*;
use super::openvr_sys::ETrackedDeviceClass::*;
use super::openvr_sys::EVREventType::*;
use std::ffi::CString;
use std::sync::Arc;
use std::ptr;
use std::cell::RefCell;
use std::mem;

pub struct OpenVRService {
    initialized: bool,
    devices: Vec<OpenVRDevicePtr>,
    system: *mut openvr::VR_IVRSystem_FnTable,
    observer: Option<Box<Fn(VRDisplayEvent)>>
}

unsafe impl Send for OpenVRService {}

impl VRService for OpenVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.initialized {
            return Ok(());
        }
        if !self.is_available() {
            return Err("Not available".into());
        }

        let mut error = EVRInitError_VRInitError_None;
        unsafe {
             openvr::VR_InitInternal(&mut error, EVRApplicationType_VRApplication_Scene);
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR Internal failed with error {}", error as u32));
        }

        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}",constants::IVRSYSTEM_VERSION)).unwrap();
            self.system = openvr::VR_GetGenericInterface(name.as_ptr(), &mut error)
                          as *mut openvr::VR_IVRSystem_FnTable;
            (*self.system).AcknowledgeQuit_UserPrompt.unwrap();
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {}", error as u32));
        }

        self.initialized = true;
        Ok(())
    }

    fn fetch_devices(&mut self) -> Result<Vec<VRDevicePtr>,String> {
        // Return cached devices if available
        if self.initialized && self.devices.len() > 0 {
            return Ok(self.clone_devices());
        }
        // Ensure that there are not initialization errors
        try!(self.initialize());

        let max_device_count: u32 = constants::K_UNMAXTRACKEDDEVICECOUNT;

        self.devices.clear();

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_HMD => {
                    self.devices.push(OpenVRDevice::new(self.system, i));
                },
                _ => {}
            }

        }

        Ok(self.clone_devices())
    }

    fn is_available(&self) -> bool {
        unsafe {
            return openvr::VR_IsHmdPresent() > 0;
        }
    }

    fn poll_events(&self) {
        let mut event: openvr::VREvent_t = unsafe { mem::uninitialized() };
        let size = mem::size_of::<openvr::VREvent_t>() as u32;
        while unsafe { (*self.system).PollNextEvent.unwrap()(&mut event, size) } != 0 {

            let event_type: openvr::EVREventType = unsafe { mem::transmute(event.eventType) };

            match event_type {
                EVREventType_VREvent_TrackedDeviceUserInteractionStarted => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {

                        self.notify_event(VRDisplayEvent::Activate(device.borrow().get_display_data(),
                                                                   VRDisplayEventReason::Mounted));
                    }
                },
                EVREventType_VREvent_TrackedDeviceUserInteractionEnded => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {

                        self.notify_event(VRDisplayEvent::Deactivate(device.borrow().get_display_data(),
                                                                     VRDisplayEventReason::Unmounted));
                    }
                },
                EVREventType_VREvent_TrackedDeviceActivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Connect(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_TrackedDeviceDeactivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Disconnect(device.borrow().device_id()))
                    }
                },
                EVREventType_VREvent_DashboardActivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Blur(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_DashboardDeactivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Focus(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_ChaperoneDataHasChanged |
                EVREventType_VREvent_IpdChanged |
                EVREventType_VREvent_TrackedDeviceUpdated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Change(device.borrow().get_display_data()))
                    }
                },
                _ => {}
            };
        }
    }

    fn set_observer(&mut self, callback: Option<Box<Fn(VRDisplayEvent)>>) {
        self.observer = callback;
    }
}

impl OpenVRService {
    pub fn new() -> VRServicePtr {
        Arc::new(RefCell::new(OpenVRService {
            initialized: false,
            devices: Vec::new(),
            system: ptr::null_mut(),
            observer: None
        }))
    }
    fn clone_devices(&self) -> Vec<VRDevicePtr> {
        self.devices.iter().map(|d| d.clone() as VRDevicePtr).collect()
    }

    pub fn get_device(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRDevicePtr> {
        self.devices.iter().find(|&d| d.borrow().index() == index)
    }

    fn notify_event(&self, event: VRDisplayEvent) {
        if let Some(ref observer) = self.observer {
            observer(event);
        }
    }
}<commit_msg>Properly shutdown OpenVR<commit_after>use {VRService, VRServicePtr, VRDevice, VRDevicePtr, VRDisplayEvent, VRDisplayEventReason};
use super::constants;
use super::device::{OpenVRDevice, OpenVRDevicePtr};
use super::openvr_sys as openvr;
use super::openvr_sys::EVRInitError::*;
use super::openvr_sys::EVRApplicationType::*;
use super::openvr_sys::ETrackedDeviceClass::*;
use super::openvr_sys::EVREventType::*;
use std::ffi::CString;
use std::sync::Arc;
use std::ptr;
use std::cell::RefCell;
use std::mem;

pub struct OpenVRService {
    initialized: bool,
    devices: Vec<OpenVRDevicePtr>,
    system: *mut openvr::VR_IVRSystem_FnTable,
    observer: Option<Box<Fn(VRDisplayEvent)>>
}

unsafe impl Send for OpenVRService {}

impl VRService for OpenVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.initialized {
            return Ok(());
        }
        if !self.is_available() {
            return Err("Not available".into());
        }

        let mut error = EVRInitError_VRInitError_None;
        unsafe {
             openvr::VR_InitInternal(&mut error, EVRApplicationType_VRApplication_Scene);
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR Internal failed with error {}", error as u32));
        }

        error = EVRInitError_VRInitError_None;
        unsafe {
            let name = CString::new(format!("FnTable:{}",constants::IVRSYSTEM_VERSION)).unwrap();
            self.system = openvr::VR_GetGenericInterface(name.as_ptr(), &mut error)
                          as *mut openvr::VR_IVRSystem_FnTable;
            (*self.system).AcknowledgeQuit_UserPrompt.unwrap();
        }

        if error as u32 != EVRInitError_VRInitError_None as u32 {
            return Err(format!("OpenVR GetGenericInterface failed with error {}", error as u32));
        }

        self.initialized = true;
        Ok(())
    }

    fn fetch_devices(&mut self) -> Result<Vec<VRDevicePtr>,String> {
        // Return cached devices if available
        if self.initialized && self.devices.len() > 0 {
            return Ok(self.clone_devices());
        }
        // Ensure that there are not initialization errors
        try!(self.initialize());

        let max_device_count: u32 = constants::K_UNMAXTRACKEDDEVICECOUNT;

        self.devices.clear();

        for i in 0..max_device_count {
            let device_class: openvr::ETrackedDeviceClass = unsafe {
                (*self.system).GetTrackedDeviceClass.unwrap()(i as openvr::TrackedDeviceIndex_t)
            };

            match device_class {
                ETrackedDeviceClass_TrackedDeviceClass_HMD => {
                    self.devices.push(OpenVRDevice::new(self.system, i));
                },
                _ => {}
            }

        }

        Ok(self.clone_devices())
    }

    fn is_available(&self) -> bool {
        unsafe {
            return openvr::VR_IsHmdPresent() > 0;
        }
    }

    fn poll_events(&self) {
        let mut event: openvr::VREvent_t = unsafe { mem::uninitialized() };
        let size = mem::size_of::<openvr::VREvent_t>() as u32;
        while unsafe { (*self.system).PollNextEvent.unwrap()(&mut event, size) } != 0 {

            let event_type: openvr::EVREventType = unsafe { mem::transmute(event.eventType) };

            match event_type {
                EVREventType_VREvent_TrackedDeviceUserInteractionStarted => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {

                        self.notify_event(VRDisplayEvent::Activate(device.borrow().get_display_data(),
                                                                   VRDisplayEventReason::Mounted));
                    }
                },
                EVREventType_VREvent_TrackedDeviceUserInteractionEnded => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {

                        self.notify_event(VRDisplayEvent::Deactivate(device.borrow().get_display_data(),
                                                                     VRDisplayEventReason::Unmounted));
                    }
                },
                EVREventType_VREvent_TrackedDeviceActivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Connect(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_TrackedDeviceDeactivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Disconnect(device.borrow().device_id()))
                    }
                },
                EVREventType_VREvent_DashboardActivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Blur(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_DashboardDeactivated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Focus(device.borrow().get_display_data()))
                    }
                },
                EVREventType_VREvent_ChaperoneDataHasChanged |
                EVREventType_VREvent_IpdChanged |
                EVREventType_VREvent_TrackedDeviceUpdated => {
                    if let Some(device) = self.get_device(event.trackedDeviceIndex) {
                        self.notify_event(VRDisplayEvent::Change(device.borrow().get_display_data()))
                    }
                },
                _ => {}
            };
        }
    }

    fn set_observer(&mut self, callback: Option<Box<Fn(VRDisplayEvent)>>) {
        self.observer = callback;
    }
}

impl Drop for OpenVRService {
    fn drop(&mut self) {
        if self.initialized {
            unsafe {
                debug!("OpenVR Shutdown");
                openvr::VR_ShutdownInternal();
            }
        }
    }
}

impl OpenVRService {
    pub fn new() -> VRServicePtr {
        Arc::new(RefCell::new(OpenVRService {
            initialized: false,
            devices: Vec::new(),
            system: ptr::null_mut(),
            observer: None
        }))
    }
    fn clone_devices(&self) -> Vec<VRDevicePtr> {
        self.devices.iter().map(|d| d.clone() as VRDevicePtr).collect()
    }

    pub fn get_device(&self, index: openvr::TrackedDeviceIndex_t) -> Option<&OpenVRDevicePtr> {
        self.devices.iter().find(|&d| d.borrow().index() == index)
    }

    fn notify_event(&self, event: VRDisplayEvent) {
        if let Some(ref observer) = self.observer {
            observer(event);
        }
    }
}<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Application launching — Startup notification for applications

use glib::translate::*;
use glib::types::{StaticType, Type};
use object::Object;
use screen::Screen;
use display::Display;
use ffi;

// FIXME: should inherit from GAppLaunchContext
pub type AppLaunchContext = Object<ffi::GdkAppLaunchContext>;

impl StaticType for AppLaunchContext {
    fn static_type() -> Type { unsafe { from_glib(ffi::gdk_app_launch_context_get_type()) } }
}

impl AppLaunchContext {
    /// Sets the display on which applications will be launched when using this context. See also
    /// `AppLaunchContext::set_screen()`.
    pub fn set_display(&self, display: &Display) {
        unsafe {
            ffi::gdk_app_launch_context_set_display(self.to_glib_none().0, display.to_glib_none().0)
        }
    }

    /// Sets the screen on which applications will be launched when using this context. See also
    /// `AppLaunchContext::set_display()`.
    ///
    /// If both screen and display are set, the screen takes priority. If neither screen or display
    /// are set, the default screen and display are used.
    pub fn set_screen(&self, screen: &Screen) {
        unsafe {
            ffi::gdk_app_launch_context_set_screen(self.to_glib_none().0, screen.to_glib_none().0)
        }
    }

    /// Sets the workspace on which applications will be launched when using this context when running
    /// under a window manager that supports multiple workspaces, as described in the
    /// Extended Window Manager Hints.
    ///
    /// When the workspace is not specified or desktop is set to -1, it is up to the window manager to
    /// pick one, typically it will be the current workspace.
    pub fn set_desktop(&self, desktop: i32) {
        unsafe { ffi::gdk_app_launch_context_set_desktop(self.to_glib_none().0, desktop) }
    }

    /// Sets the timestamp of self. The timestamp should ideally be taken from the event that triggered
    /// the launch.
    ///
    /// Window managers can use this information to avoid moving the focus to the newly launched application
    /// when the user is busy typing in another window. This is also known as 'focus stealing prevention'.
    pub fn set_timestamp(&self, timestamp: u32) {
        unsafe { ffi::gdk_app_launch_context_set_timestamp(self.to_glib_none().0, timestamp) }
    }

    /*pub fn set_icon(&self, icon: GIO::Icon) {
        unsafe { ffi::gdk_app_launch_context_set_timestamp(self.to_glib_none().0, icon.to_glib_none().0) }
    }*/

    /// Sets the icon for applications that are launched with this context. The icon_name will be
    /// interpreted in the same way as the Icon field in desktop files. See also `AppLaunchContext::set_icon()`.
    ///
    /// If both icon and icon_name are set, the icon_name takes priority. If neither icon or icon_name is set,
    /// the icon is taken from either the file that is passed to launched application or from the GAppInfo for
    /// the launched application itself.
    pub fn set_icon_name(&self, icon_name: &str) {
        unsafe {
            ffi::gdk_app_launch_context_set_icon_name(self.to_glib_none().0,
                                                      icon_name.to_glib_none().0)
        }
    }
}
<commit_msg>Add missing function<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! Application launching — Startup notification for applications

use glib::translate::*;
use glib::types::{StaticType, Type};
use object::Object;
use screen::Screen;
use display::Display;
use ffi;

// FIXME: should inherit from GAppLaunchContext
pub type AppLaunchContext = Object<ffi::GdkAppLaunchContext>;

impl StaticType for AppLaunchContext {
    fn static_type() -> Type { unsafe { from_glib(ffi::gdk_app_launch_context_get_type()) } }
}

impl AppLaunchContext {
    /// Creates a new AppLaunchContext.
    pub fn new() -> AppLaunchContext {
        unsafe { from_glib_full(ffi::gdk_app_launch_context_new()) }
    }

    /// Sets the display on which applications will be launched when using this context. See also
    /// `AppLaunchContext::set_screen()`.
    pub fn set_display(&self, display: &Display) {
        unsafe {
            ffi::gdk_app_launch_context_set_display(self.to_glib_none().0, display.to_glib_none().0)
        }
    }

    /// Sets the screen on which applications will be launched when using this context. See also
    /// `AppLaunchContext::set_display()`.
    ///
    /// If both screen and display are set, the screen takes priority. If neither screen or display
    /// are set, the default screen and display are used.
    pub fn set_screen(&self, screen: &Screen) {
        unsafe {
            ffi::gdk_app_launch_context_set_screen(self.to_glib_none().0, screen.to_glib_none().0)
        }
    }

    /// Sets the workspace on which applications will be launched when using this context when running
    /// under a window manager that supports multiple workspaces, as described in the
    /// Extended Window Manager Hints.
    ///
    /// When the workspace is not specified or desktop is set to -1, it is up to the window manager to
    /// pick one, typically it will be the current workspace.
    pub fn set_desktop(&self, desktop: i32) {
        unsafe { ffi::gdk_app_launch_context_set_desktop(self.to_glib_none().0, desktop) }
    }

    /// Sets the timestamp of self. The timestamp should ideally be taken from the event that triggered
    /// the launch.
    ///
    /// Window managers can use this information to avoid moving the focus to the newly launched application
    /// when the user is busy typing in another window. This is also known as 'focus stealing prevention'.
    pub fn set_timestamp(&self, timestamp: u32) {
        unsafe { ffi::gdk_app_launch_context_set_timestamp(self.to_glib_none().0, timestamp) }
    }

    /*pub fn set_icon(&self, icon: GIO::Icon) {
        unsafe { ffi::gdk_app_launch_context_set_timestamp(self.to_glib_none().0, icon.to_glib_none().0) }
    }*/

    /// Sets the icon for applications that are launched with this context. The icon_name will be
    /// interpreted in the same way as the Icon field in desktop files. See also `AppLaunchContext::set_icon()`.
    ///
    /// If both icon and icon_name are set, the icon_name takes priority. If neither icon or icon_name is set,
    /// the icon is taken from either the file that is passed to launched application or from the GAppInfo for
    /// the launched application itself.
    pub fn set_icon_name(&self, icon_name: &str) {
        unsafe {
            ffi::gdk_app_launch_context_set_icon_name(self.to_glib_none().0,
                                                      icon_name.to_glib_none().0)
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, Write};

#[derive(Debug)]
pub struct Question {
    pub _type: String,
    pub name: String,
    pub message: String,
}

impl Question {
    pub fn new<S: Into<String>>(name: S, message: S) -> Question {
        Question {
            _type: "input".to_string(),
            name: name.into(),
            message: message.into(),
        }
    }
}

#[derive(Debug)]
pub struct Answer {
    pub _type: String,
    pub name: String,
    pub value: String,
}

impl Answer {
    pub fn new<S: Into<String>>(name: S, value: S) -> Answer {
        Answer {
            _type: "input".to_string(),
            name: name.into(),
            value: value.into(),
        }
    }
}

pub fn ask_question<R, W>(reader: &mut R, mut writer: &mut W, question: &Question) -> Answer
    where R: BufRead,
          W: Write {
    write!(&mut writer, "{}\n", question.message).unwrap();

    let mut input = String::new();
    match reader.read_line(&mut input) {
        Ok(_) => Answer::new(question.name.to_owned(), input.trim().to_string()),
        Err(error) => panic!("Unable to read line for {}: {}", question.name, error),
    }
}

pub fn ask_questions<R, W>(reader: &mut R, writer: &mut W, questions: &Vec<Question>) -> Vec<Answer>
    where R: BufRead,
          W: Write {
    let answers: Vec<Answer> = questions.iter().map(move |question| {
        ask_question(reader, writer, &question)
    }).collect();
    let non_empty_answers: Vec<Answer> = answers.into_iter()
                                                .filter(|answer| !answer.value.is_empty())
                                                .collect();
    non_empty_answers
}

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

    use std::io::Cursor;
    use std::str::from_utf8;
    use super::ask_question;

    #[test]
    fn can_instantiate_question() {
        let expected_type = "input";
        let expected_name = "";
        let expected_message = "";

        let question = Question::new(expected_name, expected_message);

        assert_eq!(expected_type, question._type);
        assert_eq!(expected_name, question.name);
        assert_eq!(expected_message, question.message);
    }

    #[test]
    fn can_instantiate_answer() {
        let expected_type = "input";
        let expected_name = "";
        let expected_value = "";

        let answer = Answer::new(expected_name, expected_value);

        assert_eq!(expected_type, answer._type);
        assert_eq!(expected_name, answer.name);
        assert_eq!(expected_value, answer.value);
    }

    #[test]
    fn can_ask_question() {
       let mut reader = Cursor::new(&b"Sebastian"[..]);
       let mut writer = Cursor::new(Vec::new());

       let question = Question::new("test-question", "What's your name?");

       let answer = ask_question(&mut reader, &mut writer, &question);

       assert_eq!(question.name, answer.name);
       assert_eq!("Sebastian", answer.value);
       assert_eq!("What's your name?\n", from_utf8(writer.get_ref()).unwrap());
   }
}
<commit_msg>test(inquirer): ask_questions now has a test<commit_after>use std::io::{BufRead, Write};

#[derive(Debug)]
pub struct Question {
    pub _type: String,
    pub name: String,
    pub message: String,
}

impl Question {
    pub fn new<S: Into<String>>(name: S, message: S) -> Question {
        Question {
            _type: "input".to_string(),
            name: name.into(),
            message: message.into(),
        }
    }
}

#[derive(Debug)]
pub struct Answer {
    pub _type: String,
    pub name: String,
    pub value: String,
}

impl Answer {
    pub fn new<S: Into<String>>(name: S, value: S) -> Answer {
        Answer {
            _type: "input".to_string(),
            name: name.into(),
            value: value.into(),
        }
    }
}

pub fn ask_question<R, W>(reader: &mut R, mut writer: &mut W, question: &Question) -> Answer
    where R: BufRead,
          W: Write {
    write!(&mut writer, "{}\n", question.message).unwrap();

    let mut input = String::new();
    match reader.read_line(&mut input) {
        Ok(_) => Answer::new(question.name.to_owned(), input.trim().to_string()),
        Err(error) => panic!("Unable to read line for {}: {}", question.name, error),
    }
}

pub fn ask_questions<R, W>(reader: &mut R, writer: &mut W, questions: &Vec<Question>) -> Vec<Answer>
    where R: BufRead,
          W: Write {
    let answers: Vec<Answer> = questions.iter().map(move |question| {
        ask_question(reader, writer, &question)
    }).collect();
    let non_empty_answers: Vec<Answer> = answers.into_iter()
                                                .filter(|answer| !answer.value.is_empty())
                                                .collect();
    non_empty_answers
}

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

    use std::io::Cursor;
    use std::str::from_utf8;
    use super::{ask_question, ask_questions};

    #[test]
    fn can_instantiate_question() {
        let expected_type = "input";
        let expected_name = "";
        let expected_message = "";

        let question = Question::new(expected_name, expected_message);

        assert_eq!(expected_type, question._type);
        assert_eq!(expected_name, question.name);
        assert_eq!(expected_message, question.message);
    }

    #[test]
    fn can_instantiate_answer() {
        let expected_type = "input";
        let expected_name = "";
        let expected_value = "";

        let answer = Answer::new(expected_name, expected_value);

        assert_eq!(expected_type, answer._type);
        assert_eq!(expected_name, answer.name);
        assert_eq!(expected_value, answer.value);
    }

    #[test]
    fn can_ask_question() {
       let mut reader = Cursor::new(&b"Sebastian\n"[..]);
       let mut writer = Cursor::new(Vec::new());

       let question = Question::new("test-question", "What's your name?");

       let answer = ask_question(&mut reader, &mut writer, &question);

       assert_eq!(question.name, answer.name);
       assert_eq!("Sebastian", answer.value);
       assert_eq!("What's your name?\n", from_utf8(writer.get_ref()).unwrap());
   }

   #[test]
   fn can_ask_questions() {
      let mut reader = Cursor::new(&b"First Answer\nSecond Answer\nThird Answer\n"[..]);
      let mut writer = Cursor::new(Vec::new());

      let questions = vec![
          Question::new(
              "first-question",
              "Tell me your first answer?",
          ),
          Question::new(
              "second-question",
              "Tell me your second answer?",
          ),
          Question::new(
              "third-question",
              "Tell me your third answer?",
          ),
      ];

      let answers = ask_questions(&mut reader, &mut writer, &questions);

      assert_eq!(questions[0].name, answers[0].name);
      assert_eq!(questions[1].name, answers[1].name);
      assert_eq!(questions[2].name, answers[2].name);

      assert_eq!("First Answer", answers[0].value);
      assert_eq!("Second Answer", answers[1].value);
      assert_eq!("Third Answer", answers[2].value);

      assert_eq!("Tell me your first answer?\nTell me your second answer?\nTell me your third answer?\n", from_utf8(writer.get_ref()).unwrap());
  }
}
<|endoftext|>


<commit_before>use std::cmp::max;

use std::sync::atomic::{AtomicUsize, Ordering};

#[derive(Default)]
pub struct Statistic {
    pub hit_count: AtomicUsize,
    pub hit_bytes: AtomicUsize,
    pub miss_count: AtomicUsize,
    pub miss_bytes: AtomicUsize,
    pub remote_count: AtomicUsize,
}

impl Statistic {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn add_hit(&self, bytes: usize) {
        self.hit_count.fetch_add(1, Ordering::Release);
        self.hit_bytes.fetch_add(bytes, Ordering::Release);
    }

    pub fn add_miss(&self, bytes: usize) {
        self.miss_count.fetch_add(1, Ordering::Release);
        self.miss_bytes.fetch_add(bytes, Ordering::Release);
    }

    pub fn inc_remote(&self) {
        self.remote_count.fetch_add(1, Ordering::Release);
    }

    pub fn to_string(&self) -> String {
        let hit_count = self.hit_count.load(Ordering::Relaxed);
        let hit_bytes = self.hit_bytes.load(Ordering::Relaxed);
        let miss_count = self.miss_count.load(Ordering::Relaxed);
        let miss_bytes = self.miss_bytes.load(Ordering::Relaxed);
        let remote_count = self.remote_count.load(Ordering::Relaxed);
        let total_count = hit_count + miss_count;
        format!(
            "Cache statistic: hit {} of {} ({} %), remote {}, read {}, write {}, total {}",
            hit_count,
            total_count,
            hit_count * 100 / max(total_count, 1),
            remote_count,
            hit_bytes,
            miss_bytes,
            hit_bytes + miss_bytes,
        )
    }
}
<commit_msg>Fix clippy error<commit_after>use std::cmp::max;

use std::fmt;
use std::sync::atomic::{AtomicUsize, Ordering};

#[derive(Default)]
pub struct Statistic {
    pub hit_count: AtomicUsize,
    pub hit_bytes: AtomicUsize,
    pub miss_count: AtomicUsize,
    pub miss_bytes: AtomicUsize,
    pub remote_count: AtomicUsize,
}

impl fmt::Display for Statistic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        let hit_count = self.hit_count.load(Ordering::Relaxed);
        let hit_bytes = self.hit_bytes.load(Ordering::Relaxed);
        let miss_count = self.miss_count.load(Ordering::Relaxed);
        let miss_bytes = self.miss_bytes.load(Ordering::Relaxed);
        let remote_count = self.remote_count.load(Ordering::Relaxed);
        let total_count = hit_count + miss_count;
        write!(
            f,
            "Cache statistic: hit {} of {} ({} %), remote {}, read {}, write {}, total {}",
            hit_count,
            total_count,
            hit_count * 100 / max(total_count, 1),
            remote_count,
            hit_bytes,
            miss_bytes,
            hit_bytes + miss_bytes,
        )
    }
}

impl Statistic {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn add_hit(&self, bytes: usize) {
        self.hit_count.fetch_add(1, Ordering::Release);
        self.hit_bytes.fetch_add(bytes, Ordering::Release);
    }

    pub fn add_miss(&self, bytes: usize) {
        self.miss_count.fetch_add(1, Ordering::Release);
        self.miss_bytes.fetch_add(bytes, Ordering::Release);
    }

    pub fn inc_remote(&self) {
        self.remote_count.fetch_add(1, Ordering::Release);
    }
}
<|endoftext|>


<commit_before>// //! Helpers to standardize decoding Algorithmia JSON responses

// use error::{Error};
// use rustc_serialize::{json, Decodable};

// pub fn decode<T: Decodable>(res_json: &str) -> Result<T, Error> {
//     match json::decode::<T>(res_json) {
//         Ok(result) => Ok(result),
//         Err(err) => Err(Error::DecoderErrorWithContext(err, res_json.into())),
//     }
// }

<commit_msg>Remove unused json helpers<commit_after><|endoftext|>


<commit_before>///! Mainly pages to be delivered and triggered from webui.
///! There are some static pages and few dynamical pages.
use super::{
    super::super::{common::config, net::subs::peer_representation::PeerRepresentation},
    WebServerState,
};
use actix_files as fs;
use actix_web::{
    http::StatusCode,
    web::{self, HttpResponse},
    Responder,
};
use regex::{Captures, Regex, Replacer};
use std::{
    ffi::OsString,
    string::String,
    sync::{Arc, Mutex},
};

/// dynamic development files
#[allow(dead_code)]
pub async fn dyn_devel_html() -> impl Responder {
    fs::NamedFile::open("src/ctrl/webui/html/main_page.html")
}

#[allow(dead_code)]
pub async fn dyn_devel_js() -> impl Responder {
    fs::NamedFile::open("src/ctrl/webui/js/app.js")
}

pub async fn single_page(state: web::Data<Arc<Mutex<WebServerState>>>) -> impl Responder {
    // change state
    let data = state.lock().unwrap();
    let id = data.id;
    *(data.nr_connections.lock().unwrap()) += 1;
    let port = data.web_port;

    let id_page = replace_static_content(*config::webui::HTML_PAGE, &id, port);

    HttpResponse::build(StatusCode::OK)
        .content_type("text/html; charset=utf-8")
        .body(id_page)
}

pub async fn bootstrap_css(path: web::Path<(String,)>) -> impl Responder {
    let css = &*path.0;
    let output = match css {
        "bootstrap.css" => Some(*config::webui::bootstrap::CSS),
        "bootstrap.css.map" => Some(*config::webui::bootstrap::CSS_MAP),
        "bootstrap.min.css" => Some(*config::webui::bootstrap::CSS_MIN),
        "bootstrap.min.css.map" => Some(*config::webui::bootstrap::CSS_MIN_MAP),
        "bootstrap-grid.css" => Some(*config::webui::bootstrap::CSS_GRID),
        "bootstrap-grid.css.map" => Some(*config::webui::bootstrap::CSS_GRID_MAP),
        "bootstrap-grid.min.css" => Some(*config::webui::bootstrap::CSS_GRID_MIN),
        "bootstrap-grid.min.css.map" => Some(*config::webui::bootstrap::CSS_GRID_MIN_MAP),
        "bootstrap-reboot.css" => Some(*config::webui::bootstrap::CSS_REBOOT),
        "bootstrap-reboot.css.map" => Some(*config::webui::bootstrap::CSS_REBOOT_MAP),
        "bootstrap-reboot.min.css" => Some(*config::webui::bootstrap::CSS_REBOOT_MIN),
        "bootstrap-reboot.min.css.map" => Some(*config::webui::bootstrap::CSS_REBOOT_MIN_MAP),
        _ => {
            error!("CSS: not found {}", css);
            None
        }
    };
    if let Some(content) = output {
        HttpResponse::build(StatusCode::OK)
            .content_type("text/css; charset=utf-8")
            .body(content)
    } else {
        HttpResponse::build(StatusCode::NOT_FOUND)
            .content_type("text/css; charset=utf-8")
            .body("")
    }
}

pub async fn bootstrap_js(path: web::Path<(String,)>) -> impl Responder {
    let js = &*path.0;
    let output = match js {
        "bootstrap.js" => Some(*config::webui::bootstrap::JS),
        "bootstrap.js.map" => Some(*config::webui::bootstrap::JS_MAP),
        "bootstrap.min.js" => Some(*config::webui::bootstrap::JS_MIN),
        "bootstrap.min.js.map" => Some(*config::webui::bootstrap::JS_MIN_MAP),
        "bootstrap.bundle.js" => Some(*config::webui::bootstrap::JS_BUNDLE),
        "bootstrap.bundle.js.map" => Some(*config::webui::bootstrap::JS_BUNDLE_MAP),
        "bootstrap.bundle.min.js" => Some(*config::webui::bootstrap::JS_BUNDLE_MIN),
        "bootstrap.bundle.min.js.map" => Some(*config::webui::bootstrap::JS_BUNDLE_MIN_MAP),
        _ => {
            error!("JS: not found {}", js);
            None
        }
    };
    if let Some(content) = output {
        HttpResponse::build(StatusCode::OK)
            .content_type("application/javascript; charset=utf-8")
            .body(content)
    } else {
        HttpResponse::build(StatusCode::NOT_FOUND)
            .content_type("application/javascript; charset=utf-8")
            .body("")
    }
}

pub async fn js_app(state: web::Data<Arc<Mutex<WebServerState>>>) -> impl Responder {
    let data = state.lock().unwrap();
    let id = data.id;
    let port = data.web_port;

    let output = replace_static_content(*config::webui::JS_APP, &id, port);
    HttpResponse::build(StatusCode::OK)
        .content_type("application/javascript; charset=utf-8")
        .body(output)
}

fn replace_static_content(html_in: &str, id: &PeerRepresentation, port: u16) -> String {
    // short inline struct

    let id_string = std::format!("{:x?}", id);
    let hostname = hostname::get()
        .unwrap_or(OsString::from("undefined"))
        .into_string()
        .unwrap_or(String::from("undefined"));

    let changers: [ReplaceStatic; 9] = [
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_URL_SOURCE,
            c: config::net::HOMEPAGE.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_WEB_ADDR,
            c: config::net::WEB_ADDR.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_WEB_PORT,
            c: port.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_WEB_SOCKET,
            c: config::net::WEB_ADDR.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PEER_HASH,
            c: id_string,
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_HOSTNAME,
            c: hostname,
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PEER_PAGE,
            c: config::webui::PEER_PAGE.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PATHS_MAX,
            c: config::data::PATHS_MAX.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_VERSION,
            c: config::net::VERSION.to_string(),
        },
    ];
    linear_LUT_replacer(html_in, &changers)
}

struct ReplaceStatic<'a> {
    r: &'a str,
    c: String,
}

#[allow(non_snake_case)]
fn linear_LUT_replacer(replace_this: &str, changers: &[ReplaceStatic]) -> String {
    lazy_static! {
        static ref RE_WINDOWS: Regex = Regex::new(
            &[
                config::webui::HTML_REPLACER_BEGIN,
                "(?P<txt>([[:alnum:]]|_)+)",
                config::webui::HTML_REPLACER_END
            ]
            .concat()
        )
        .unwrap();
    }
    let rep = HTMLReplacer::new(changers);
    RE_WINDOWS.replace_all(replace_this, rep).to_string()
}

struct HTMLReplacer<'a> {
    changers: &'a [ReplaceStatic<'a>],
}

impl<'a> HTMLReplacer<'a> {
    fn new(changers: &'a [ReplaceStatic<'a>]) -> Self {
        Self {
            changers: &changers,
        }
    }
}

impl<'a> Replacer for HTMLReplacer<'a> {
    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
        let matched = caps.name("txt").unwrap();
        dst.push_str(
            &self
                .changers
                .iter()
                .find(|r| r.r == matched.as_str())
                .and_then(|s| Some(s.c.clone()))
                .or_else(|| {
                    warn!("This HTML-replacer is not defined '{}'", matched.as_str());
                    Some("".to_string())
                })
                .unwrap(),
        );
    }
}

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

    #[test]
    #[allow(non_snake_case)]
    fn quick_replace_LUT_tests() {
        let changers: [ReplaceStatic; 6] = [
            ReplaceStatic {
                r: config::webui::HTML_REPLACE_STATIC_URL_SOURCE,
                c: config::net::HOMEPAGE.to_string(),
            },
            ReplaceStatic {
                r: "CAT",
                c: "cat".to_string(),
            },
            ReplaceStatic {
                r: "JUMP",
                c: "jumped".to_string(),
            },
            ReplaceStatic {
                r: "ON",
                c: " on ".to_string(),
            },
            ReplaceStatic {
                r: "TABLE",
                c: "table".to_string(),
            },
            ReplaceStatic {
                r: config::webui::HTML_REPLACE_VERSION,
                c: config::net::VERSION.to_string(),
            },
        ];
        let replace_this = "The <!---CAT---> <!---JUMP---><!---ON--->the <!---TABLE--->.";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "The cat jumped on the table.";
        assert_eq!(return_value, expect);

        let replace_this = "<!---JUMP---> <!---CAT---> <!---ON---> <!---CAT--->.";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "jumped cat  on  cat.";
        assert_eq!(return_value, expect);

        let replace_this = "<!---JUMP---><!---CAT--->";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "jumpedcat";
        assert_eq!(return_value, expect);

        let replace_this = "<!--JUMP--><!---CAT---><!--- -->";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "<!--JUMP-->cat<!--- -->";
        assert_eq!(return_value, expect);

        let replace_this = [
            "<!--JUMP--><!---",
            &config::webui::HTML_REPLACE_VERSION,
            "---><!--- -->",
        ]
        .concat();
        let return_value = linear_LUT_replacer(&replace_this, &changers);
        let expect = ["<!--JUMP-->", &config::net::VERSION, "<!--- -->"].concat();
        assert_eq!(return_value, expect);
    }
}
<commit_msg>[collection] remark on optimization<commit_after>///! Mainly pages to be delivered and triggered from webui.
///! There are some static pages and few dynamical pages.
use super::{
    super::super::{common::config, net::subs::peer_representation::PeerRepresentation},
    WebServerState,
};
use actix_files as fs;
use actix_web::{
    http::StatusCode,
    web::{self, HttpResponse},
    Responder,
};
use regex::{Captures, Regex, Replacer};
use std::{
    ffi::OsString,
    string::String,
    sync::{Arc, Mutex},
};

/// dynamic development files
#[allow(dead_code)]
pub async fn dyn_devel_html() -> impl Responder {
    fs::NamedFile::open("src/ctrl/webui/html/main_page.html")
}

#[allow(dead_code)]
pub async fn dyn_devel_js() -> impl Responder {
    fs::NamedFile::open("src/ctrl/webui/js/app.js")
}

pub async fn single_page(state: web::Data<Arc<Mutex<WebServerState>>>) -> impl Responder {
    // change state
    let data = state.lock().unwrap();
    let id = data.id;
    *(data.nr_connections.lock().unwrap()) += 1;
    let port = data.web_port;

    let id_page = replace_static_content(*config::webui::HTML_PAGE, &id, port);

    HttpResponse::build(StatusCode::OK)
        .content_type("text/html; charset=utf-8")
        .body(id_page)
}

pub async fn bootstrap_css(path: web::Path<(String,)>) -> impl Responder {
    let css = &*path.0;
    let output = match css {
        "bootstrap.css" => Some(*config::webui::bootstrap::CSS),
        "bootstrap.css.map" => Some(*config::webui::bootstrap::CSS_MAP),
        "bootstrap.min.css" => Some(*config::webui::bootstrap::CSS_MIN),
        "bootstrap.min.css.map" => Some(*config::webui::bootstrap::CSS_MIN_MAP),
        "bootstrap-grid.css" => Some(*config::webui::bootstrap::CSS_GRID),
        "bootstrap-grid.css.map" => Some(*config::webui::bootstrap::CSS_GRID_MAP),
        "bootstrap-grid.min.css" => Some(*config::webui::bootstrap::CSS_GRID_MIN),
        "bootstrap-grid.min.css.map" => Some(*config::webui::bootstrap::CSS_GRID_MIN_MAP),
        "bootstrap-reboot.css" => Some(*config::webui::bootstrap::CSS_REBOOT),
        "bootstrap-reboot.css.map" => Some(*config::webui::bootstrap::CSS_REBOOT_MAP),
        "bootstrap-reboot.min.css" => Some(*config::webui::bootstrap::CSS_REBOOT_MIN),
        "bootstrap-reboot.min.css.map" => Some(*config::webui::bootstrap::CSS_REBOOT_MIN_MAP),
        _ => {
            error!("CSS: not found {}", css);
            None
        }
    };
    if let Some(content) = output {
        HttpResponse::build(StatusCode::OK)
            .content_type("text/css; charset=utf-8")
            .body(content)
    } else {
        HttpResponse::build(StatusCode::NOT_FOUND)
            .content_type("text/css; charset=utf-8")
            .body("")
    }
}

pub async fn bootstrap_js(path: web::Path<(String,)>) -> impl Responder {
    let js = &*path.0;
    let output = match js {
        "bootstrap.js" => Some(*config::webui::bootstrap::JS),
        "bootstrap.js.map" => Some(*config::webui::bootstrap::JS_MAP),
        "bootstrap.min.js" => Some(*config::webui::bootstrap::JS_MIN),
        "bootstrap.min.js.map" => Some(*config::webui::bootstrap::JS_MIN_MAP),
        "bootstrap.bundle.js" => Some(*config::webui::bootstrap::JS_BUNDLE),
        "bootstrap.bundle.js.map" => Some(*config::webui::bootstrap::JS_BUNDLE_MAP),
        "bootstrap.bundle.min.js" => Some(*config::webui::bootstrap::JS_BUNDLE_MIN),
        "bootstrap.bundle.min.js.map" => Some(*config::webui::bootstrap::JS_BUNDLE_MIN_MAP),
        _ => {
            error!("JS: not found {}", js);
            None
        }
    };
    if let Some(content) = output {
        HttpResponse::build(StatusCode::OK)
            .content_type("application/javascript; charset=utf-8")
            .body(content)
    } else {
        HttpResponse::build(StatusCode::NOT_FOUND)
            .content_type("application/javascript; charset=utf-8")
            .body("")
    }
}

pub async fn js_app(state: web::Data<Arc<Mutex<WebServerState>>>) -> impl Responder {
    let data = state.lock().unwrap();
    let id = data.id;
    let port = data.web_port;

    let output = replace_static_content(*config::webui::JS_APP, &id, port);
    HttpResponse::build(StatusCode::OK)
        .content_type("application/javascript; charset=utf-8")
        .body(output)
}

fn replace_static_content(html_in: &str, id: &PeerRepresentation, port: u16) -> String {
    // short inline struct

    let id_string = std::format!("{:x?}", id);
    let hostname = hostname::get()
        .unwrap_or(OsString::from("undefined"))
        .into_string()
        .unwrap_or(String::from("undefined"));

    let changers: [ReplaceStatic; 9] = [
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_URL_SOURCE,
            c: config::net::HOMEPAGE.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_WEB_ADDR,
            c: config::net::WEB_ADDR.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_STATIC_WEB_PORT,
            c: port.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_WEB_SOCKET,
            c: config::net::WEB_ADDR.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PEER_HASH,
            c: id_string,
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_HOSTNAME,
            c: hostname,
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PEER_PAGE,
            c: config::webui::PEER_PAGE.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_PATHS_MAX,
            c: config::data::PATHS_MAX.to_string(),
        },
        ReplaceStatic {
            r: config::webui::HTML_REPLACE_VERSION,
            c: config::net::VERSION.to_string(),
        },
    ];
    // todo: look, how to optimize this ...
    linear_LUT_replacer(html_in, &changers)
}

struct ReplaceStatic<'a> {
    r: &'a str,
    c: String,
}

#[allow(non_snake_case)]
fn linear_LUT_replacer(replace_this: &str, changers: &[ReplaceStatic]) -> String {
    lazy_static! {
        static ref RE_WINDOWS: Regex = Regex::new(
            &[
                config::webui::HTML_REPLACER_BEGIN,
                "(?P<txt>([[:alnum:]]|_)+)",
                config::webui::HTML_REPLACER_END
            ]
            .concat()
        )
        .unwrap();
    }
    let rep = HTMLReplacer::new(changers);
    RE_WINDOWS.replace_all(replace_this, rep).to_string()
}

struct HTMLReplacer<'a> {
    changers: &'a [ReplaceStatic<'a>],
}

impl<'a> HTMLReplacer<'a> {
    fn new(changers: &'a [ReplaceStatic<'a>]) -> Self {
        Self {
            changers: &changers,
        }
    }
}

impl<'a> Replacer for HTMLReplacer<'a> {
    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
        let matched = caps.name("txt").unwrap();
        dst.push_str(
            &self
                .changers
                .iter()
                .find(|r| r.r == matched.as_str())
                .and_then(|s| Some(s.c.clone()))
                .or_else(|| {
                    warn!("This HTML-replacer is not defined '{}'", matched.as_str());
                    Some("".to_string())
                })
                .unwrap(),
        );
    }
}

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

    #[test]
    #[allow(non_snake_case)]
    fn quick_replace_LUT_tests() {
        let changers: [ReplaceStatic; 6] = [
            ReplaceStatic {
                r: config::webui::HTML_REPLACE_STATIC_URL_SOURCE,
                c: config::net::HOMEPAGE.to_string(),
            },
            ReplaceStatic {
                r: "CAT",
                c: "cat".to_string(),
            },
            ReplaceStatic {
                r: "JUMP",
                c: "jumped".to_string(),
            },
            ReplaceStatic {
                r: "ON",
                c: " on ".to_string(),
            },
            ReplaceStatic {
                r: "TABLE",
                c: "table".to_string(),
            },
            ReplaceStatic {
                r: config::webui::HTML_REPLACE_VERSION,
                c: config::net::VERSION.to_string(),
            },
        ];
        let replace_this = "The <!---CAT---> <!---JUMP---><!---ON--->the <!---TABLE--->.";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "The cat jumped on the table.";
        assert_eq!(return_value, expect);

        let replace_this = "<!---JUMP---> <!---CAT---> <!---ON---> <!---CAT--->.";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "jumped cat  on  cat.";
        assert_eq!(return_value, expect);

        let replace_this = "<!---JUMP---><!---CAT--->";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "jumpedcat";
        assert_eq!(return_value, expect);

        let replace_this = "<!--JUMP--><!---CAT---><!--- -->";
        let return_value = linear_LUT_replacer(replace_this, &changers);
        let expect = "<!--JUMP-->cat<!--- -->";
        assert_eq!(return_value, expect);

        let replace_this = [
            "<!--JUMP--><!---",
            &config::webui::HTML_REPLACE_VERSION,
            "---><!--- -->",
        ]
        .concat();
        let return_value = linear_LUT_replacer(&replace_this, &changers);
        let expect = ["<!--JUMP-->", &config::net::VERSION, "<!--- -->"].concat();
        assert_eq!(return_value, expect);
    }
}
<|endoftext|>


<commit_before>//
// Copyright:: Copyright (c) 2016 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use clap::{App, Arg, ArgMatches, SubCommand};
use cli::Options;
use cli::arguments::{local_arg, patchset_arg, pipeline_arg, project_arg, project_specific_args,
                     u_e_s_o_args, value_of};
use config::Config;
use fips;
use project;
use types::DeliveryResult;

pub const SUBCOMMAND_NAME: &'static str = "job";

#[derive(Debug)]
pub struct JobClapOptions<'n> {
    pub stage: &'n str,
    pub phases: &'n str,
    pub change: &'n str,
    pub pipeline: &'n str,
    pub job_root: &'n str,
    pub project: &'n str,
    pub user: &'n str,
    pub server: &'n str,
    pub ent: &'n str,
    pub org: &'n str,
    pub patchset: &'n str,
    pub change_id: &'n str,
    pub git_url: &'n str,
    pub shasum: &'n str,
    pub branch: &'n str,
    pub skip_default: bool,
    pub local: bool,
    pub docker_image: &'n str,
    pub fips: bool,
    pub fips_git_port: &'n str,
    pub fips_custom_cert_filename: &'n str,
}

impl<'n> Default for JobClapOptions<'n> {
    fn default() -> Self {
        JobClapOptions {
            stage: "",
            phases: "",
            change: "",
            pipeline: "master",
            job_root: "",
            project: "",
            user: "",
            server: "",
            ent: "",
            org: "",
            patchset: "",
            change_id: "",
            git_url: "",
            shasum: "",
            branch: "",
            skip_default: false,
            local: false,
            docker_image: "",
            fips: false,
            fips_git_port: "",
            fips_custom_cert_filename: "",
        }
    }
}

impl<'n> JobClapOptions<'n> {
    pub fn new(matches: &'n ArgMatches<'n>) -> Self {
        JobClapOptions {
            stage: value_of(&matches, "stage"),
            phases: value_of(matches, "phases"),
            change: value_of(&matches, "change"),
            pipeline: value_of(&matches, "pipeline"),
            job_root: value_of(&matches, "job-root"),
            project: value_of(&matches, "project"),
            user: value_of(&matches, "user"),
            server: value_of(&matches, "server"),
            ent: value_of(&matches, "ent"),
            org: value_of(&matches, "org"),
            patchset: value_of(&matches, "patchset"),
            change_id: value_of(&matches, "change-id"),
            git_url: value_of(&matches, "git-url"),
            shasum: value_of(&matches, "shasum"),
            branch: value_of(&matches, "branch"),
            skip_default: matches.is_present("skip-default"),
            local: matches.is_present("local"),
            docker_image: value_of(&matches, "docker"),
            fips: matches.is_present("fips"),
            fips_git_port: value_of(&matches, "fips-git-port"),
            fips_custom_cert_filename: value_of(&matches, "fips-custom-cert-filename"),
        }
    }
}

impl<'n> Options for JobClapOptions<'n> {
    fn merge_options_and_config(&self, config: Config) -> DeliveryResult<Config> {
        let project = try!(project::project_or_from_cwd(&self.project));

        let new_config = config
            .set_pipeline(&self.pipeline)
            .set_user(with_default(&self.user, "you", &&self.local))
            .set_server(with_default(&self.server, "localhost", &&self.local))
            .set_enterprise(with_default(&self.ent, "local", &&self.local))
            .set_organization(with_default(&self.org, "workstation", &&self.local))
            .set_project(&project);

        fips::merge_fips_options_and_config(
            self.fips,
            self.fips_git_port,
            self.fips_custom_cert_filename,
            new_config,
        )
    }
}

fn with_default<'a>(val: &'a str, default: &'a str, local: &bool) -> &'a str {
    if !local || !val.is_empty() {
        val
    } else {
        default
    }
}

pub fn clap_subcommand<'c>() -> App<'c, 'c> {
    SubCommand::with_name(SUBCOMMAND_NAME)
        .about("Run one or more phase jobs")
        .args(&vec![patchset_arg(), project_arg(), local_arg()])
        .args(&make_arg_vec![
            "-j --job-root=[root] 'Path to the job root'",
            "-g --git-url=[url] 'Git URL (-u -s -e -o ignored if used)'",
            "-C --change=[change] 'Feature branch name'",
            "-b --branch=[branch] 'Branch to merge'",
            "-S --shasum=[gitsha] 'Git SHA of change'",
            "--change-id=[id] 'The change ID'",
            "--skip-default 'skip default'",
            "--docker=[image] 'Docker image'"
        ])
        .args_from_usage(
            "<stage> 'Stage for the run'
                          <phases> 'One or more phases'",
        )
        .args(&u_e_s_o_args())
        .args(&pipeline_arg())
        .args(&project_specific_args())
}
<commit_msg>Duplicate --a2-mode from setup to job<commit_after>//
// Copyright:: Copyright (c) 2016 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use clap::{App, Arg, ArgMatches, SubCommand};
use cli::Options;
use cli::arguments::{local_arg, patchset_arg, pipeline_arg, project_arg, project_specific_args,
                     u_e_s_o_args, a2_mode_arg, value_of};
use config::Config;
use fips;
use project;
use types::DeliveryResult;

pub const SUBCOMMAND_NAME: &'static str = "job";

#[derive(Debug)]
pub struct JobClapOptions<'n> {
    pub stage: &'n str,
    pub phases: &'n str,
    pub change: &'n str,
    pub pipeline: &'n str,
    pub job_root: &'n str,
    pub project: &'n str,
    pub user: &'n str,
    pub server: &'n str,
    pub ent: &'n str,
    pub org: &'n str,
    pub patchset: &'n str,
    pub change_id: &'n str,
    pub git_url: &'n str,
    pub shasum: &'n str,
    pub branch: &'n str,
    pub skip_default: bool,
    pub local: bool,
    pub docker_image: &'n str,
    pub fips: bool,
    pub fips_git_port: &'n str,
    pub fips_custom_cert_filename: &'n str,
    pub a2_mode: bool,
}

impl<'n> Default for JobClapOptions<'n> {
    fn default() -> Self {
        JobClapOptions {
            stage: "",
            phases: "",
            change: "",
            pipeline: "master",
            job_root: "",
            project: "",
            user: "",
            server: "",
            ent: "",
            org: "",
            patchset: "",
            change_id: "",
            git_url: "",
            shasum: "",
            branch: "",
            skip_default: false,
            local: false,
            docker_image: "",
            fips: false,
            fips_git_port: "",
            fips_custom_cert_filename: "",
            a2_mode: false,
        }
    }
}

impl<'n> JobClapOptions<'n> {
    pub fn new(matches: &'n ArgMatches<'n>) -> Self {
        JobClapOptions {
            stage: value_of(&matches, "stage"),
            phases: value_of(matches, "phases"),
            change: value_of(&matches, "change"),
            pipeline: value_of(&matches, "pipeline"),
            job_root: value_of(&matches, "job-root"),
            project: value_of(&matches, "project"),
            user: value_of(&matches, "user"),
            server: value_of(&matches, "server"),
            ent: value_of(&matches, "ent"),
            org: value_of(&matches, "org"),
            patchset: value_of(&matches, "patchset"),
            change_id: value_of(&matches, "change-id"),
            git_url: value_of(&matches, "git-url"),
            shasum: value_of(&matches, "shasum"),
            branch: value_of(&matches, "branch"),
            skip_default: matches.is_present("skip-default"),
            local: matches.is_present("local"),
            docker_image: value_of(&matches, "docker"),
            fips: matches.is_present("fips"),
            fips_git_port: value_of(&matches, "fips-git-port"),
            fips_custom_cert_filename: value_of(&matches, "fips-custom-cert-filename"),
            a2_mode: matches.is_present("a2-mode"),
        }
    }
}

impl<'n> Options for JobClapOptions<'n> {
    fn merge_options_and_config(&self, config: Config) -> DeliveryResult<Config> {
        let project = try!(project::project_or_from_cwd(&self.project));

        let mut new_config = config
            .set_pipeline(&self.pipeline)
            .set_user(with_default(&self.user, "you", &&self.local))
            .set_server(with_default(&self.server, "localhost", &&self.local))
            .set_enterprise(with_default(&self.ent, "local", &&self.local))
            .set_organization(with_default(&self.org, "workstation", &&self.local))
            .set_project(&project)
            .set_a2_mode(self.a2_mode);

        // A2 mode requires SAML right now
        if self.a2_mode {
            new_config.saml = Some(true)
        }

        fips::merge_fips_options_and_config(
            self.fips,
            self.fips_git_port,
            self.fips_custom_cert_filename,
            new_config,
        )
    }
}

fn with_default<'a>(val: &'a str, default: &'a str, local: &bool) -> &'a str {
    if !local || !val.is_empty() {
        val
    } else {
        default
    }
}

pub fn clap_subcommand<'c>() -> App<'c, 'c> {
    SubCommand::with_name(SUBCOMMAND_NAME)
        .about("Run one or more phase jobs")
        .args(&vec![patchset_arg(), project_arg(), local_arg()])
        .args(&make_arg_vec![
            "-j --job-root=[root] 'Path to the job root'",
            "-g --git-url=[url] 'Git URL (-u -s -e -o ignored if used)'",
            "-C --change=[change] 'Feature branch name'",
            "-b --branch=[branch] 'Branch to merge'",
            "-S --shasum=[gitsha] 'Git SHA of change'",
            "--change-id=[id] 'The change ID'",
            "--skip-default 'skip default'",
            "--docker=[image] 'Docker image'"
        ])
        .args_from_usage(
            "<stage> 'Stage for the run'
                          <phases> 'One or more phases'",
        )
        .args(&u_e_s_o_args())
        .args(&pipeline_arg())
        .args(&project_specific_args())
		.args(&vec![a2_mode_arg()])
}
<|endoftext|>