<commit_before>use futures::sync::oneshot;
use futures::{future, Future};
use std::borrow::Cow;
use std::mem;
use std::sync::mpsc::{RecvError, TryRecvError};
use std::thread;
use std;

use core::config::{Bitrate, PlayerConfig};
use core::session::Session;
use core::util::{self, SpotifyId, Subfile};

use audio_backend::Sink;
use audio::{AudioFile, AudioDecrypt};
use audio::{VorbisDecoder, VorbisPacket};
use metadata::{FileFormat, Track, Metadata};
use mixer::AudioFilter;

pub struct Player {
    commands: Option<std::sync::mpsc::Sender<PlayerCommand>>,
    thread_handle: Option<thread::JoinHandle<()>>,
}

struct PlayerInternal {
    session: Session,
    config: PlayerConfig,
    commands: std::sync::mpsc::Receiver<PlayerCommand>,

    state: PlayerState,
    sink: Box<Sink>,
    audio_filter: Option<Box<AudioFilter + Send>>,
}

enum PlayerCommand {
    Load(SpotifyId, bool, u32, oneshot::Sender<()>),
    Play,
    Pause,
    Stop,
    Seek(u32),
}

impl Player {
    pub fn new<F>(config: PlayerConfig, session: Session,
                  audio_filter: Option<Box<AudioFilter + Send>>,
                  sink_builder: F) -> Player
        where F: FnOnce() -> Box<Sink> + Send + 'static
    {
        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();

        let handle = thread::spawn(move || {
            debug!("new Player[{}]", session.session_id());

            let internal = PlayerInternal {
                session: session,
                config: config,
                commands: cmd_rx,

                state: PlayerState::Stopped,
                sink: sink_builder(),
                audio_filter: audio_filter,
            };

            internal.run();
        });

        Player {
            commands: Some(cmd_tx),
            thread_handle: Some(handle),
        }
    }

    fn command(&self, cmd: PlayerCommand) {
        self.commands.as_ref().unwrap().send(cmd).unwrap();
    }

    pub fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32)
        -> oneshot::Receiver<()>
    {
        let (tx, rx) = oneshot::channel();
        self.command(PlayerCommand::Load(track, start_playing, position_ms, tx));

        rx
    }

    pub fn play(&self) {
        self.command(PlayerCommand::Play)
    }

    pub fn pause(&self) {
        self.command(PlayerCommand::Pause)
    }

    pub fn stop(&self) {
        self.command(PlayerCommand::Stop)
    }

    pub fn seek(&self, position_ms: u32) {
        self.command(PlayerCommand::Seek(position_ms));
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        debug!("Shutting down player thread ...");
        self.commands = None;
        if let Some(handle) = self.thread_handle.take() {
            match handle.join() {
                Ok(_) => (),
                Err(_) => error!("Player thread panicked!")
            }
        }
    }
}

type Decoder = VorbisDecoder<Subfile<AudioDecrypt<AudioFile>>>;
enum PlayerState {
    Stopped,
    Paused {
        decoder: Decoder,
        end_of_track: oneshot::Sender<()>,
    },
    Playing {
        decoder: Decoder,
        end_of_track: oneshot::Sender<()>,
    },

    Invalid,
}

impl PlayerState {
    fn is_playing(&self) -> bool {
        use self::PlayerState::*;
        match *self {
            Stopped | Paused { .. } => false,
            Playing { .. } => true,
            Invalid => panic!("invalid state"),
        }
    }

    fn decoder(&mut self) -> Option<&mut Decoder> {
        use self::PlayerState::*;
        match *self {
            Stopped => None,
            Paused { ref mut decoder, .. } |
            Playing { ref mut decoder, .. } => Some(decoder),
            Invalid => panic!("invalid state"),
        }
    }

    fn signal_end_of_track(self) {
        use self::PlayerState::*;
        match self {
            Paused { end_of_track, .. } |
            Playing { end_of_track, .. } => {
                end_of_track.complete(())
            }

            Stopped => warn!("signal_end_of_track from stopped state"),
            Invalid => panic!("invalid state"),
        }
    }

    fn paused_to_playing(&mut self) {
        use self::PlayerState::*;
        match ::std::mem::replace(self, Invalid) {
            Paused { decoder, end_of_track } => {
                *self = Playing {
                    decoder: decoder,
                    end_of_track: end_of_track,
                };
            }
            _ => panic!("invalid state"),
        }
    }

    fn playing_to_paused(&mut self) {
        use self::PlayerState::*;
        match ::std::mem::replace(self, Invalid) {
            Playing { decoder, end_of_track } => {
                *self = Paused {
                    decoder: decoder,
                    end_of_track: end_of_track,
                };
            }
            _ => panic!("invalid state"),
        }
    }
}

impl PlayerInternal {
    fn run(mut self) {
        loop {
            let cmd = if self.state.is_playing() {
                match self.commands.try_recv() {
                    Ok(cmd) => Some(cmd),
                    Err(TryRecvError::Empty) => None,
                    Err(TryRecvError::Disconnected) => return,
                }
            } else {
                match self.commands.recv() {
                    Ok(cmd) => Some(cmd),
                    Err(RecvError) => return,
                }
            };

            if let Some(cmd) = cmd {
                self.handle_command(cmd);
            }

            let packet = if let PlayerState::Playing { ref mut decoder, .. } = self.state {
                Some(decoder.next_packet().expect("Vorbis error"))
            } else { None };

            if let Some(packet) = packet {
                self.handle_packet(packet);
            }
        }
    }

    fn handle_packet(&mut self, packet: Option<VorbisPacket>) {
        match packet {
            Some(mut packet) => {
                if let Some(ref editor) = self.audio_filter {
                    editor.modify_stream(&mut packet.data_mut())
                };

                self.sink.write(&packet.data()).unwrap();
            }

            None => {
                self.sink.stop().unwrap();
                self.run_onstop();

                let old_state = mem::replace(&mut self.state, PlayerState::Stopped);
                old_state.signal_end_of_track();
            }
        }
    }

    fn handle_command(&mut self, cmd: PlayerCommand) {
        debug!("command={:?}", cmd);
        match cmd {
            PlayerCommand::Load(track_id, play, position, end_of_track) => {
                if self.state.is_playing() {
                    self.sink.stop().unwrap();
                }

                match self.load_track(track_id, position as i64) {
                    Some(decoder) => {
                        if play {
                            if !self.state.is_playing() {
                                self.run_onstart();
                            }
                            self.sink.start().unwrap();

                            self.state = PlayerState::Playing {
                                decoder: decoder,
                                end_of_track: end_of_track,
                            };
                        } else {
                            if self.state.is_playing() {
                                self.run_onstop();
                            }

                            self.state = PlayerState::Paused {
                                decoder: decoder,
                                end_of_track: end_of_track,
                            };
                        }
                    }

                    None => {
                        end_of_track.complete(());
                        if self.state.is_playing() {
                            self.run_onstop();
                        }
                    }
                }
            }

            PlayerCommand::Seek(position) => {
                if let Some(decoder) = self.state.decoder() {
                    match decoder.seek(position as i64) {
                        Ok(_) => (),
                        Err(err) => error!("Vorbis error: {:?}", err),
                    }
                } else {
                    warn!("Player::seek called from invalid state");
                }
            }

            PlayerCommand::Play => {
                if let PlayerState::Paused { .. } = self.state {
                    self.state.paused_to_playing();

                    self.run_onstart();
                    self.sink.start().unwrap();
                } else {
                    warn!("Player::play called from invalid state");
                }
            }

            PlayerCommand::Pause => {
                if let PlayerState::Playing { .. } = self.state {
                    self.state.playing_to_paused();

                    self.sink.stop().unwrap();
                    self.run_onstop();
                } else {
                    warn!("Player::pause called from invalid state");
                }
            }

            PlayerCommand::Stop => {
                match self.state {
                    PlayerState::Playing { .. } => {
                        self.sink.stop().unwrap();
                        self.run_onstop();
                        self.state = PlayerState::Stopped;
                    }
                    PlayerState::Paused { .. } => {
                        self.state = PlayerState::Stopped;
                    },
                    PlayerState::Stopped => {
                        warn!("Player::stop called from invalid state");
                    }
                    PlayerState::Invalid => panic!("invalid state"),
                }
            }
        }
    }

    fn run_onstart(&self) {
        if let Some(ref program) = self.config.onstart {
            util::run_program(program)
        }
    }

    fn run_onstop(&self) {
        if let Some(ref program) = self.config.onstop {
            util::run_program(program)
        }
    }

    fn find_available_alternative<'a>(&self, track: &'a Track) -> Option<Cow<'a, Track>> {
        if track.available {
            Some(Cow::Borrowed(track))
        } else {
            let alternatives = track.alternatives
                .iter()
                .map(|alt_id| {
                    Track::get(&self.session, *alt_id)
                });
            let alternatives = future::join_all(alternatives).wait().unwrap();

            alternatives.into_iter().find(|alt| alt.available).map(Cow::Owned)
        }
    }

    fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<Decoder> {
        let track = Track::get(&self.session, track_id).wait().unwrap();

        info!("Loading track \"{}\"", track.name);

        let track = match self.find_available_alternative(&track) {
            Some(track) => track,
            None => {
                warn!("Track \"{}\" is not available", track.name);
                return None;
            }
        };

        let format = match self.config.bitrate {
            Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96,
            Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160,
            Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320,
        };

        let file_id = match track.files.get(&format) {
            Some(&file_id) => file_id,
            None => {
                warn!("Track \"{}\" is not available in format {:?}", track.name, format);
                return None;
            }
        };

        let key = self.session.audio_key().request(track.id, file_id).wait().unwrap();

        let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap();
        let audio_file = Subfile::new(AudioDecrypt::new(key, encrypted_file), 0xa7);

        let mut decoder = VorbisDecoder::new(audio_file).unwrap();

        match decoder.seek(position) {
            Ok(_) => (),
            Err(err) => error!("Vorbis error: {:?}", err),
        }

        info!("Track \"{}\" loaded", track.name);

        Some(decoder)
    }
}

impl Drop for PlayerInternal {
    fn drop(&mut self) {
        debug!("drop Player[{}]", self.session.session_id());
    }
}

impl ::std::fmt::Debug for PlayerCommand {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            PlayerCommand::Load(track, play, position, _) => {
                f.debug_tuple("Load")
                 .field(&track)
                 .field(&play)
                 .field(&position)
                 .finish()
            }
            PlayerCommand::Play => {
                f.debug_tuple("Play").finish()
            }
            PlayerCommand::Pause => {
                f.debug_tuple("Pause").finish()
            }
            PlayerCommand::Stop => {
                f.debug_tuple("Stop").finish()
            }
            PlayerCommand::Seek(position) => {
                f.debug_tuple("Seek")
                 .field(&position)
                 .finish()
            }
        }
    }
}
<commit_msg>Handle audio sink errors in the player<commit_after>use futures::sync::oneshot;
use futures::{future, Future};
use std::borrow::Cow;
use std::mem;
use std::sync::mpsc::{RecvError, TryRecvError, RecvTimeoutError};
use std::thread;
use std::time::Duration;
use std;

use core::config::{Bitrate, PlayerConfig};
use core::session::Session;
use core::util::{self, SpotifyId, Subfile};

use audio_backend::Sink;
use audio::{AudioFile, AudioDecrypt};
use audio::{VorbisDecoder, VorbisPacket};
use metadata::{FileFormat, Track, Metadata};
use mixer::AudioFilter;

pub struct Player {
    commands: Option<std::sync::mpsc::Sender<PlayerCommand>>,
    thread_handle: Option<thread::JoinHandle<()>>,
}

struct PlayerInternal {
    session: Session,
    config: PlayerConfig,
    commands: std::sync::mpsc::Receiver<PlayerCommand>,

    state: PlayerState,
    sink: Box<Sink>,
    sink_running: bool,
    audio_filter: Option<Box<AudioFilter + Send>>,
}

enum PlayerCommand {
    Load(SpotifyId, bool, u32, oneshot::Sender<()>),
    Play,
    Pause,
    Stop,
    Seek(u32),
}

impl Player {
    pub fn new<F>(config: PlayerConfig, session: Session,
                  audio_filter: Option<Box<AudioFilter + Send>>,
                  sink_builder: F) -> Player
        where F: FnOnce() -> Box<Sink> + Send + 'static
    {
        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();

        let handle = thread::spawn(move || {
            debug!("new Player[{}]", session.session_id());

            let internal = PlayerInternal {
                session: session,
                config: config,
                commands: cmd_rx,

                state: PlayerState::Stopped,
                sink: sink_builder(),
                sink_running: false,
                audio_filter: audio_filter,
            };

            internal.run();
        });

        Player {
            commands: Some(cmd_tx),
            thread_handle: Some(handle),
        }
    }

    fn command(&self, cmd: PlayerCommand) {
        self.commands.as_ref().unwrap().send(cmd).unwrap();
    }

    pub fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32)
        -> oneshot::Receiver<()>
    {
        let (tx, rx) = oneshot::channel();
        self.command(PlayerCommand::Load(track, start_playing, position_ms, tx));

        rx
    }

    pub fn play(&self) {
        self.command(PlayerCommand::Play)
    }

    pub fn pause(&self) {
        self.command(PlayerCommand::Pause)
    }

    pub fn stop(&self) {
        self.command(PlayerCommand::Stop)
    }

    pub fn seek(&self, position_ms: u32) {
        self.command(PlayerCommand::Seek(position_ms));
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        debug!("Shutting down player thread ...");
        self.commands = None;
        if let Some(handle) = self.thread_handle.take() {
            match handle.join() {
                Ok(_) => (),
                Err(_) => error!("Player thread panicked!")
            }
        }
    }
}

type Decoder = VorbisDecoder<Subfile<AudioDecrypt<AudioFile>>>;
enum PlayerState {
    Stopped,
    Paused {
        decoder: Decoder,
        end_of_track: oneshot::Sender<()>,
    },
    Playing {
        decoder: Decoder,
        end_of_track: oneshot::Sender<()>,
    },

    Invalid,
}

impl PlayerState {
    fn is_playing(&self) -> bool {
        use self::PlayerState::*;
        match *self {
            Stopped | Paused { .. } => false,
            Playing { .. } => true,
            Invalid => panic!("invalid state"),
        }
    }

    fn decoder(&mut self) -> Option<&mut Decoder> {
        use self::PlayerState::*;
        match *self {
            Stopped => None,
            Paused { ref mut decoder, .. } |
            Playing { ref mut decoder, .. } => Some(decoder),
            Invalid => panic!("invalid state"),
        }
    }

    fn signal_end_of_track(self) {
        use self::PlayerState::*;
        match self {
            Paused { end_of_track, .. } |
            Playing { end_of_track, .. } => {
                end_of_track.complete(())
            }

            Stopped => warn!("signal_end_of_track from stopped state"),
            Invalid => panic!("invalid state"),
        }
    }

    fn paused_to_playing(&mut self) {
        use self::PlayerState::*;
        match ::std::mem::replace(self, Invalid) {
            Paused { decoder, end_of_track } => {
                *self = Playing {
                    decoder: decoder,
                    end_of_track: end_of_track,
                };
            }
            _ => panic!("invalid state"),
        }
    }

    fn playing_to_paused(&mut self) {
        use self::PlayerState::*;
        match ::std::mem::replace(self, Invalid) {
            Playing { decoder, end_of_track } => {
                *self = Paused {
                    decoder: decoder,
                    end_of_track: end_of_track,
                };
            }
            _ => panic!("invalid state"),
        }
    }
}

impl PlayerInternal {
    fn run(mut self) {
        loop {
            let cmd = if self.state.is_playing() {
                if self.sink_running
                {
                    match self.commands.try_recv() {
                        Ok(cmd) => Some(cmd),
                        Err(TryRecvError::Empty) => None,
                        Err(TryRecvError::Disconnected) => return,
                    }
                }
                else
                {
                    match self.commands.recv_timeout(Duration::from_secs(5)) {
                        Ok(cmd) => Some(cmd),
                        Err(RecvTimeoutError::Timeout) => None,
                        Err(RecvTimeoutError::Disconnected) => return,
                    }
                }
            } else {
                match self.commands.recv() {
                    Ok(cmd) => Some(cmd),
                    Err(RecvError) => return,
                }
            };

            if let Some(cmd) = cmd {
                self.handle_command(cmd);
            }

            if self.state.is_playing() && ! self.sink_running {
                self.start_sink();
            }

            if self.sink_running {
                let packet = if let PlayerState::Playing { ref mut decoder, .. } = self.state {
                    Some(decoder.next_packet().expect("Vorbis error"))
                } else {
                    None
                };

                if let Some(packet) = packet {
                    self.handle_packet(packet);
                }
            }
        }
    }

    fn start_sink(&mut self) {
        match self.sink.start() {
            Ok(()) => self.sink_running = true,
            Err(err) => error!("Could not start audio: {}", err),
        }
    }

    fn stop_sink_if_running(&mut self) {
        if self.sink_running {
            self.stop_sink();
        }
    }

    fn stop_sink(&mut self) {
        self.sink.stop().unwrap();
        self.sink_running = false;
    }

    fn handle_packet(&mut self, packet: Option<VorbisPacket>) {
        match packet {
            Some(mut packet) => {
                if let Some(ref editor) = self.audio_filter {
                    editor.modify_stream(&mut packet.data_mut())
                };

                if let Err(err) = self.sink.write(&packet.data()) {
                    error!("Could not write audio: {}", err);
                    self.stop_sink();
                }
            }

            None => {
                self.stop_sink();
                self.run_onstop();

                let old_state = mem::replace(&mut self.state, PlayerState::Stopped);
                old_state.signal_end_of_track();
            }
        }
    }

    fn handle_command(&mut self, cmd: PlayerCommand) {
        debug!("command={:?}", cmd);
        match cmd {
            PlayerCommand::Load(track_id, play, position, end_of_track) => {
                if self.state.is_playing() {
                    self.stop_sink_if_running();
                }

                match self.load_track(track_id, position as i64) {
                    Some(decoder) => {
                        if play {
                            if !self.state.is_playing() {
                                self.run_onstart();
                            }
                            self.start_sink();

                            self.state = PlayerState::Playing {
                                decoder: decoder,
                                end_of_track: end_of_track,
                            };
                        } else {
                            if self.state.is_playing() {
                                self.run_onstop();
                            }

                            self.state = PlayerState::Paused {
                                decoder: decoder,
                                end_of_track: end_of_track,
                            };
                        }
                    }

                    None => {
                        end_of_track.complete(());
                        if self.state.is_playing() {
                            self.run_onstop();
                        }
                    }
                }
            }

            PlayerCommand::Seek(position) => {
                if let Some(decoder) = self.state.decoder() {
                    match decoder.seek(position as i64) {
                        Ok(_) => (),
                        Err(err) => error!("Vorbis error: {:?}", err),
                    }
                } else {
                    warn!("Player::seek called from invalid state");
                }
            }

            PlayerCommand::Play => {
                if let PlayerState::Paused { .. } = self.state {
                    self.state.paused_to_playing();

                    self.run_onstart();
                    self.start_sink();
                } else {
                    warn!("Player::play called from invalid state");
                }
            }

            PlayerCommand::Pause => {
                if let PlayerState::Playing { .. } = self.state {
                    self.state.playing_to_paused();

                    self.stop_sink_if_running();
                    self.run_onstop();
                } else {
                    warn!("Player::pause called from invalid state");
                }
            }

            PlayerCommand::Stop => {
                match self.state {
                    PlayerState::Playing { .. } => {
                        self.stop_sink_if_running();
                        self.run_onstop();
                        self.state = PlayerState::Stopped;
                    }
                    PlayerState::Paused { .. } => {
                        self.state = PlayerState::Stopped;
                    },
                    PlayerState::Stopped => {
                        warn!("Player::stop called from invalid state");
                    }
                    PlayerState::Invalid => panic!("invalid state"),
                }
            }
        }
    }

    fn run_onstart(&self) {
        if let Some(ref program) = self.config.onstart {
            util::run_program(program)
        }
    }

    fn run_onstop(&self) {
        if let Some(ref program) = self.config.onstop {
            util::run_program(program)
        }
    }

    fn find_available_alternative<'a>(&self, track: &'a Track) -> Option<Cow<'a, Track>> {
        if track.available {
            Some(Cow::Borrowed(track))
        } else {
            let alternatives = track.alternatives
                .iter()
                .map(|alt_id| {
                    Track::get(&self.session, *alt_id)
                });
            let alternatives = future::join_all(alternatives).wait().unwrap();

            alternatives.into_iter().find(|alt| alt.available).map(Cow::Owned)
        }
    }

    fn load_track(&self, track_id: SpotifyId, position: i64) -> Option<Decoder> {
        let track = Track::get(&self.session, track_id).wait().unwrap();

        info!("Loading track \"{}\"", track.name);

        let track = match self.find_available_alternative(&track) {
            Some(track) => track,
            None => {
                warn!("Track \"{}\" is not available", track.name);
                return None;
            }
        };

        let format = match self.config.bitrate {
            Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96,
            Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160,
            Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320,
        };

        let file_id = match track.files.get(&format) {
            Some(&file_id) => file_id,
            None => {
                warn!("Track \"{}\" is not available in format {:?}", track.name, format);
                return None;
            }
        };

        let key = self.session.audio_key().request(track.id, file_id).wait().unwrap();

        let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap();
        let audio_file = Subfile::new(AudioDecrypt::new(key, encrypted_file), 0xa7);

        let mut decoder = VorbisDecoder::new(audio_file).unwrap();

        match decoder.seek(position) {
            Ok(_) => (),
            Err(err) => error!("Vorbis error: {:?}", err),
        }

        info!("Track \"{}\" loaded", track.name);

        Some(decoder)
    }
}

impl Drop for PlayerInternal {
    fn drop(&mut self) {
        debug!("drop Player[{}]", self.session.session_id());
    }
}

impl ::std::fmt::Debug for PlayerCommand {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            PlayerCommand::Load(track, play, position, _) => {
                f.debug_tuple("Load")
                 .field(&track)
                 .field(&play)
                 .field(&position)
                 .finish()
            }
            PlayerCommand::Play => {
                f.debug_tuple("Play").finish()
            }
            PlayerCommand::Pause => {
                f.debug_tuple("Pause").finish()
            }
            PlayerCommand::Stop => {
                f.debug_tuple("Stop").finish()
            }
            PlayerCommand::Seek(position) => {
                f.debug_tuple("Seek")
                 .field(&position)
                 .finish()
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Raster implementation.

use std::slice;

use ::RasterMut;

/// Depth of a raster.
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
pub enum RasterDepth {
    Depth8,
    Depth16,
}

impl<'a> RasterMut<'a> {
    /// Allocate a new raster for the given destination buffer slice.
    ///
    /// # Examples
    ///
    /// ```
    /// const IMG_W: usize = 320;
    /// const IMG_H: usize = 200;
    /// let mut buf = [0; 3 * IMG_W * IMG_H];
    ///
    /// bayer::RasterMut::new(
    ///         IMG_W, IMG_H, bayer::RasterDepth::Depth8,
    ///         &mut buf);
    /// ```
    pub fn new(w: usize, h: usize, depth: RasterDepth, buf: &'a mut [u8])
            -> Self {
        let bytes_per_pixel = depth.bytes_per_pixel();
        let stride = w.checked_mul(bytes_per_pixel).expect("overflow");
        Self::with_offset(0, 0, w, h, stride, depth, buf)
    }

    /// Allocate a new raster for the given destination buffer slice.
    /// Stride is in number of bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// const IMG_W: usize = 320;
    /// const IMG_H: usize = 200;
    /// let mut buf = [0; 3 * IMG_W * IMG_H];
    ///
    /// bayer::RasterMut::with_offset(
    ///         0, 0, IMG_W, IMG_H, 3 * IMG_W, bayer::RasterDepth::Depth8,
    ///         &mut buf);
    /// ```
    pub fn with_offset(
            x: usize, y: usize, w: usize, h: usize, stride: usize,
            depth: RasterDepth, buf: &'a mut [u8])
            -> Self {
        let x1 = x.checked_add(w).expect("overflow");
        let y1 = y.checked_add(h).expect("overflow");
        let bytes_per_pixel = depth.bytes_per_pixel();
        assert!(x < x1 && x1.checked_mul(bytes_per_pixel).expect("overflow") <= stride && h > 0);
        assert!(stride.checked_mul(y1).expect("overflow") <= buf.len());
        assert_eq!(stride % bytes_per_pixel, 0);

        RasterMut {
            x: x,
            y: y,
            w: w,
            h: h,
            stride: stride,
            depth: depth,
            buf: buf,
        }
    }

    /// Borrow a mutable u8 row slice.
    ///
    /// # Panics
    ///
    /// Panics if the raster is not 8-bpp.
    pub fn borrow_row_u8_mut(&mut self, y: usize)
            -> &mut [u8] {
        assert!(self.depth == RasterDepth::Depth8);
        assert!(y < self.h);

        let bytes_per_pixel = 3;
        let start = self.stride * (self.y + y) + bytes_per_pixel * self.x;
        let end = start + bytes_per_pixel * self.w;
        &mut self.buf[start..end]
    }

    /// Borrow a mutable u16 row slice.
    ///
    /// # Panics
    ///
    /// Panics if the raster is not 16-bpp.
    pub fn borrow_row_u16_mut(&mut self, y: usize)
            -> &mut [u16] {
        assert!(self.depth == RasterDepth::Depth16);
        assert!(y < self.h);

        let bytes_per_pixel = 6;
        let start = self.stride * (self.y + y) + bytes_per_pixel * self.x;
        let end = start + bytes_per_pixel * self.w;
        let s = &mut self.buf[start..end];

        unsafe {
            slice::from_raw_parts_mut(s.as_mut_ptr() as *mut u16, 3 * self.w)
        }
    }
}

impl RasterDepth {
    /// The number of bytes per pixel for a raster of the given depth.
    fn bytes_per_pixel(self) -> usize {
        match self {
            RasterDepth::Depth8 => 3,
            RasterDepth::Depth16 => 6,
        }
    }
}

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

    #[test]
    #[should_panic]
    fn test_raster_mut_overflow() {
        let mut buf = [0; 1];
        let _ = RasterMut::new(
                ::std::usize::MAX, ::std::usize::MAX, RasterDepth::Depth8, &mut buf);
    }

    #[test]
    fn test_borrow_row_u16_mut() {
        let expected = [
            0x00,0x00, 0x01,0x01, 0x02,0x02,
            0x03,0x03, 0x04,0x04, 0x05,0x05,
            0x06,0x06, 0x07,0x07, 0x08,0x08,
            0x09,0x09, 0x0A,0x0A, 0x0B,0x0B ];

        const IMG_W: usize = 4;
        const IMG_H: usize = 1;
        let mut buf = [0u8; 6 * IMG_W * IMG_H];

        {
            let mut dst = RasterMut::new(
                    IMG_W, IMG_H, RasterDepth::Depth16, &mut buf);
            let row = dst.borrow_row_u16_mut(0);

            for (i, elt) in row.iter_mut().enumerate() {
                // Work around different endians.
                let i = i as u16;
                *elt = (i << 8) | i;
            }
        }

        assert_eq!(&buf[0..6 * IMG_W * IMG_H], &expected[..]);
    }
}
<commit_msg>Clean up.<commit_after>//! Raster implementation.

use std::slice;

use ::RasterMut;

/// Depth of a raster.
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
pub enum RasterDepth {
    Depth8,
    Depth16,
}

impl<'a> RasterMut<'a> {
    /// Allocate a new raster for the given destination buffer slice.
    ///
    /// # Examples
    ///
    /// ```
    /// const IMG_W: usize = 320;
    /// const IMG_H: usize = 200;
    /// let mut buf = [0; 3 * IMG_W * IMG_H];
    ///
    /// bayer::RasterMut::new(
    ///         IMG_W, IMG_H, bayer::RasterDepth::Depth8,
    ///         &mut buf);
    /// ```
    pub fn new(w: usize, h: usize, depth: RasterDepth, buf: &'a mut [u8])
            -> Self {
        let bytes_per_pixel = depth.bytes_per_pixel();
        let stride = w.checked_mul(bytes_per_pixel).expect("overflow");
        Self::with_offset(0, 0, w, h, stride, depth, buf)
    }

    /// Allocate a new raster for the given destination buffer slice.
    /// Stride is in number of bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// const IMG_W: usize = 320;
    /// const IMG_H: usize = 200;
    /// let mut buf = [0; 3 * IMG_W * IMG_H];
    ///
    /// bayer::RasterMut::with_offset(
    ///         0, 0, IMG_W, IMG_H, 3 * IMG_W, bayer::RasterDepth::Depth8,
    ///         &mut buf);
    /// ```
    pub fn with_offset(
            x: usize, y: usize, w: usize, h: usize, stride: usize,
            depth: RasterDepth, buf: &'a mut [u8])
            -> Self {
        let x1 = x.checked_add(w).expect("overflow");
        let y1 = y.checked_add(h).expect("overflow");
        let bytes_per_pixel = depth.bytes_per_pixel();
        assert!(x < x1 && x1.checked_mul(bytes_per_pixel).expect("overflow") <= stride && h > 0);
        assert!(stride.checked_mul(y1).expect("overflow") <= buf.len());
        assert_eq!(stride % bytes_per_pixel, 0);

        RasterMut {
            x, y, w, h, stride, depth, buf,
        }
    }

    /// Borrow a mutable u8 row slice.
    ///
    /// # Panics
    ///
    /// Panics if the raster is not 8-bpp.
    pub fn borrow_row_u8_mut(&mut self, y: usize)
            -> &mut [u8] {
        assert!(self.depth == RasterDepth::Depth8);
        assert!(y < self.h);

        let bytes_per_pixel = 3;
        let start = self.stride * (self.y + y) + bytes_per_pixel * self.x;
        let end = start + bytes_per_pixel * self.w;
        &mut self.buf[start..end]
    }

    /// Borrow a mutable u16 row slice.
    ///
    /// # Panics
    ///
    /// Panics if the raster is not 16-bpp.
    pub fn borrow_row_u16_mut(&mut self, y: usize)
            -> &mut [u16] {
        assert!(self.depth == RasterDepth::Depth16);
        assert!(y < self.h);

        let bytes_per_pixel = 6;
        let start = self.stride * (self.y + y) + bytes_per_pixel * self.x;
        let end = start + bytes_per_pixel * self.w;
        let s = &mut self.buf[start..end];

        unsafe {
            slice::from_raw_parts_mut(s.as_mut_ptr() as *mut u16, 3 * self.w)
        }
    }
}

impl RasterDepth {
    /// The number of bytes per pixel for a raster of the given depth.
    fn bytes_per_pixel(self) -> usize {
        match self {
            RasterDepth::Depth8 => 3,
            RasterDepth::Depth16 => 6,
        }
    }
}

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

    #[test]
    #[should_panic]
    fn test_raster_mut_overflow() {
        let mut buf = [0; 1];
        let _ = RasterMut::new(
                ::std::usize::MAX, ::std::usize::MAX, RasterDepth::Depth8, &mut buf);
    }

    #[test]
    fn test_borrow_row_u16_mut() {
        let expected = [
            0x00,0x00, 0x01,0x01, 0x02,0x02,
            0x03,0x03, 0x04,0x04, 0x05,0x05,
            0x06,0x06, 0x07,0x07, 0x08,0x08,
            0x09,0x09, 0x0A,0x0A, 0x0B,0x0B ];

        const IMG_W: usize = 4;
        const IMG_H: usize = 1;
        let mut buf = [0u8; 6 * IMG_W * IMG_H];

        {
            let mut dst = RasterMut::new(
                    IMG_W, IMG_H, RasterDepth::Depth16, &mut buf);
            let row = dst.borrow_row_u16_mut(0);

            for (i, elt) in row.iter_mut().enumerate() {
                // Work around different endians.
                let i = i as u16;
                *elt = (i << 8) | i;
            }
        }

        assert_eq!(&buf[0..6 * IMG_W * IMG_H], &expected[..]);
    }
}
<|endoftext|>


<commit_before>use regex::{Captures, Regex};
use term::{self, color, Terminal};

/// Finds all matches for the given pattern on the given subject and prints out
/// the subject with matches highlighted.
pub fn print_matches(pattern: &str, subject: &str) {
    // Attempt to compile the given regex pattern.
    let regex = match Regex::new(&pattern) {
        Ok(result) => { result },
        Err(_) => {
            println!("Invalid regular expression pattern '{}'.", pattern);
            return;
        }
    };

    // Loop over each match in the subject and pretty-print the match as well as
    // any trailing, non-matching text preceding the match.
    let mut last_index = 0;
    for captures in regex.captures_iter(subject) {
        let positions = captures.pos(0).unwrap();

        print!("{}", &subject[last_index .. positions.0]);
        print_captures(captures);
        last_index = positions.1;
    }

    // Print trailing unmatched text if there is any.
    if last_index < subject.len() {
        print!("{}", &subject[last_index ..]);
    }
}

/// Prints out a capture group using color formatting.
fn print_captures(captures: Captures) {
    let mut terminal = term::stdout().unwrap();
    let color_cycle = [color::BLUE, color::GREEN, color::MAGENTA, color::YELLOW];
    let mut color_index = 1;

    // Get the string of the entire match and the relative offset in the subject.
    let string = captures.at(0).unwrap();
    let offset = captures.pos(0).unwrap().0;

    // To highlight sub-matches, divide the match string into a series of
    // resizeable regions that store their range and their color. As we loop
    // over each capture, find the parent region the current capture group fits
    // into, and split the region in two, with a new region for the current
    // capture between it. Worst-case time is O(3n^2), where n is the number of
    // subgroups in the regular expression.
    let mut regions: Vec<(usize, usize, u16)> = Vec::new();
    regions.push((offsets.0, offsets.1, color::BLUE));

    for i in 1..captures.len() {
        let pos = captures.pos(i).unwrap();

        for j in 0..regions.len() {
            if pos.0 >= regions[j].0 && pos.1 <= regions[j].1 {
                // Define two new regions that overlap the old one.
                let middle = (pos.0, pos.1, color_cycle[color_index]);
                let right = (pos.1, regions[j].1, regions[j].2);
                // Shrink the old region to be the leftmost one.
                regions[j].1 = pos.0;

                // Insert the new regions.
                regions.insert(j + 1, middle);
                regions.insert(j + 2, right);

                // Choose next color next time.
                color_index = (color_index + 1) % 4;
                break;
            }
        }
    }

    // Now print out the prepared regions; this part is pretty easy, since the
    // region parser already did all the work.
    for i in 0..regions.len() {
        terminal.bg(regions[i].2).unwrap();
        print!("{}", &string[regions[i].0 - offset .. regions[i].1 - offset]);
    }

    // Reset coloring to normal.
    terminal.reset().unwrap();
}
<commit_msg>Fix offsets undefined<commit_after>use regex::{Captures, Regex};
use term::{self, color, Terminal};

/// Finds all matches for the given pattern on the given subject and prints out
/// the subject with matches highlighted.
pub fn print_matches(pattern: &str, subject: &str) {
    // Attempt to compile the given regex pattern.
    let regex = match Regex::new(&pattern) {
        Ok(result) => { result },
        Err(_) => {
            println!("Invalid regular expression pattern '{}'.", pattern);
            return;
        }
    };

    // Loop over each match in the subject and pretty-print the match as well as
    // any trailing, non-matching text preceding the match.
    let mut last_index = 0;
    for captures in regex.captures_iter(subject) {
        let positions = captures.pos(0).unwrap();

        print!("{}", &subject[last_index .. positions.0]);
        print_captures(captures);
        last_index = positions.1;
    }

    // Print trailing unmatched text if there is any.
    if last_index < subject.len() {
        print!("{}", &subject[last_index ..]);
    }
}

/// Prints out a capture group using color formatting.
fn print_captures(captures: Captures) {
    let mut terminal = term::stdout().unwrap();
    let color_cycle = [color::BLUE, color::GREEN, color::MAGENTA, color::YELLOW];
    let mut color_index = 1;

    // Get the string of the entire match and the relative offset in the subject.
    let string = captures.at(0).unwrap();
    let offsets = captures.pos(0).unwrap();

    // To highlight sub-matches, divide the match string into a series of
    // resizeable regions that store their range and their color. As we loop
    // over each capture, find the parent region the current capture group fits
    // into, and split the region in two, with a new region for the current
    // capture between it. Worst-case time is O(3n^2), where n is the number of
    // subgroups in the regular expression.
    let mut regions: Vec<(usize, usize, u16)> = Vec::new();
    regions.push((offsets.0, offsets.1, color::BLUE));

    for i in 1..captures.len() {
        let pos = captures.pos(i).unwrap();

        for j in 0..regions.len() {
            if pos.0 >= regions[j].0 && pos.1 <= regions[j].1 {
                // Define two new regions that overlap the old one.
                let middle = (pos.0, pos.1, color_cycle[color_index]);
                let right = (pos.1, regions[j].1, regions[j].2);
                // Shrink the old region to be the leftmost one.
                regions[j].1 = pos.0;

                // Insert the new regions.
                regions.insert(j + 1, middle);
                regions.insert(j + 2, right);

                // Choose next color next time.
                color_index = (color_index + 1) % 4;
                break;
            }
        }
    }

    // Now print out the prepared regions; this part is pretty easy, since the
    // region parser already did all the work.
    for i in 0..regions.len() {
        terminal.bg(regions[i].2).unwrap();
        print!("{}", &string[regions[i].0 - offsets.0 .. regions[i].1 - offsets.0]);
    }

    // Reset coloring to normal.
    terminal.reset().unwrap();
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use ansi;

pub struct Screen {
  pub tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    Screen { tty: tty, original_stty_state: current_stty_state, height: rows, width: cols }
  }

  pub fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

pub struct Terminal {
  input: File,
  output: File,
}

impl Terminal {
  pub fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    Terminal { input: input_file, output: output_file }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  pub fn getchar(&mut self) -> u8 {
    let byte = self.input.read_byte().unwrap();
    byte
  }

  pub fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice());
  }

  pub fn writeln(&mut self, s: &str) {
    self.output.write_line(s);
  }

  pub fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}
<commit_msg>Fix the remaining warnings<commit_after>#![allow(dead_code)]

use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use ansi;

pub struct Screen {
  pub tty: Terminal,
  original_stty_state: Vec<u8>,
  pub height: u16,
  pub width: u16,
}

impl Screen {
  pub fn open_screen() -> Screen {
    let mut tty = Terminal::open_terminal();
    let current_stty_state = tty.stty(&["-g"]);
    tty.initialize();
    let (cols, rows) = tty.winsize().unwrap();
    Screen { tty: tty, original_stty_state: current_stty_state, height: rows, width: cols }
  }

  pub fn restore_tty(&mut self) {
    self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
  }

  pub fn move_cursor(&mut self, line: u16, column: u16) {
    self.tty.write(ansi::setpos(line, column).as_slice());
  }
}

impl Drop for Screen {
  fn drop(&mut self) {
    self.restore_tty();
  }
}

pub struct Terminal {
  input: File,
  output: File,
}

impl Terminal {
  pub fn open_terminal() -> Terminal {
    let term_path = Path::new("/dev/tty");
    let input_file = File::open_mode(&term_path, Open, Read).unwrap();
    let output_file = File::open_mode(&term_path, Open, Write).unwrap();
    Terminal { input: input_file, output: output_file }
  }

  fn initialize(&mut self) {
    self.stty(&["raw", "-echo", "cbreak"]);
  }

  fn stty(&mut self, args: &[&str]) -> Vec<u8> {
    let term_input = File::open_mode(&Path::new("/dev/tty"), Open, Read).unwrap();
    let fd = term_input.as_raw_fd();
    let mut process = match Command::new("stty").args(args).stdin(InheritFd(fd)).spawn() {
      Ok(p) => p,
      Err(e) => panic!("Command failed: {}", e),
    };

    process.stdout.as_mut().unwrap().read_to_end().unwrap()
  }

  pub fn getchar(&mut self) -> u8 {
    let byte = self.input.read_byte().unwrap();
    byte
  }

  pub fn write(&mut self, s: &[u8]) {
    self.output.write(s.as_slice()).unwrap();
  }

  pub fn writeln(&mut self, s: &str) {
    self.output.write_line(s).unwrap();
  }

  pub fn winsize(&self) -> Option<(u16, u16)> {
    extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
    const TIOCGWINSZ: c_ulong = 0x40087468;

    #[repr(C)]
    struct TermSize {
      rows: c_ushort,
      cols: c_ushort,
      x: c_ushort,
      y: c_ushort,
    }

    let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
    if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
      Some((size.cols as u16, size.rows as u16))
    } else {
      None
    }
  }
}
<|endoftext|>


<commit_before>use std;
use std::cmp;
use std::io::stdout;
use std::io::Write;
use std::thread;
use std::time;

use termion;
use termion::clear;
use termion::color;
use termion::cursor;
use termion::input::MouseTerminal;
use termion::raw::IntoRawMode;
use termion::raw::RawTerminal;

use core::Core;
use update::Update;

pub struct Screen {
    pub stdout: MouseTerminal<RawTerminal<std::io::Stdout>>,
    pub size: (u16, u16),
}

impl Screen {
    pub fn new() -> Screen {
        let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
        write!(stdout, "{}", clear::All).unwrap();
        stdout.flush().unwrap();
        Screen {
            size: termion::terminal_size().unwrap(),
            stdout: stdout,
        }
    }

    // TODO: handle lines that are longer than terminal width.
    // Should we wrap them or truncate them?
    pub fn redraw(&mut self, update: &Update) {
        unimplemented!();
        /*
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();

        let nb_lines = cmp::min(update.lines.len(), self.size.1 as usize);
        if nb_lines > 0 {
            for line in update.lines.iter().take(nb_lines - 1) {
                write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();

                if let Some(selection) = line.selection {
                    let start = selection.0 as usize;
                    let end = selection.1 as usize;
                    let mut str_before = String::new();
                    for c in line.text.chars().take(start) {
                        str_before.push(c);
                    }
                    let mut str_selection = String::new();
                    for c in line.text.chars().skip(start).take(end) {
                        str_selection.push(c);
                    }
                    let mut str_after = String::new();
                    for c in line.text.chars().skip(end) {
                        str_after.push(c);
                    }
                    write!(self.stdout, "{}{}{}{}{}{}",
                           termion::style::Reset,
                           str_before,
                           termion::color::Bg(color::Red),
                           str_selection,
                           termion::style::Reset,
                           str_after).unwrap();
                } else {
                    self.stdout.write_all(line.text.as_bytes()).unwrap();
                }
            }

            // If the last line has a trailing \n, we need to remove it
            let mut last_line = update.lines[nb_lines - 1].text.clone();
            match last_line.pop() {
                Some('\n') | None => {
                },
                Some(c) => {
                    last_line.push(c);
                }
            }
            write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
            self.stdout.write_all(last_line.as_bytes()).unwrap();
        }

        let cursor_line_idx = update.scroll_to.0 - update.first_line;
        let cursor_line = update.lines[cursor_line_idx as usize].text.clone();
        let mut cols =  0;
        for c in cursor_line.chars().take(update.scroll_to.1 as usize) {
            if c == '\t' {
                cols += 4;
            } else {
                cols += 1;
            }
        }
        self.scroll(cols + 1, cursor_line_idx + 1);
        */
    }

    pub fn scroll(&mut self, col: u64, line: u64) {
        write!(self.stdout, "{}", cursor::Goto((col + 1) as u16, (line + 1) as u16)).unwrap();
        self.stdout.flush().unwrap();
    }

    pub fn init(&mut self) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        self.stdout.flush().unwrap();
    }

    pub fn update(&mut self, core: &mut Core) {
        // TODO: check if terminal size changed. If so, send a `render_line` command to the backend,
        // and a `scroll` command for future updates.
        if let Ok(msg) = core.update_rx.try_recv() {
            let msg_list = msg.as_array().unwrap();
            let (method, params) = (msg_list[0].as_str().unwrap(), msg_list[1].as_object().unwrap());
            if method == "update" {
                let update = Update::from_value(params.get("update").unwrap());
                self.redraw(&update);
            } else if method == "scroll_to" {
                let (col, line) = (params.get("col").unwrap().as_u64().unwrap(), params.get("line").unwrap().as_u64().unwrap());
                self.scroll(col, line);
            } else if method == "set_style" {
                // TODO: ???
            } else {
                info!("Unknown request from backend {:?}", method);
            }
        } else {
            thread::sleep(time::Duration::from_millis(10));
        }
    }
}
<commit_msg>Adds a method to draw a View onto the Screen<commit_after>use std;
use std::cmp;
use std::io::stdout;
use std::io::Write;
use std::thread;
use std::time;

use termion;
use termion::clear;
use termion::color;
use termion::cursor;
use termion::input::MouseTerminal;
use termion::raw::IntoRawMode;
use termion::raw::RawTerminal;

use core::Core;
use update::Update;
use view::View;

pub struct Screen {
    pub stdout: MouseTerminal<RawTerminal<std::io::Stdout>>,
    pub size: (u16, u16),
}

impl Screen {
    pub fn new() -> Screen {
        let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
        write!(stdout, "{}", clear::All).unwrap();
        stdout.flush().unwrap();
        Screen {
            size: termion::terminal_size().unwrap(),
            stdout: stdout,
        }
    }

    // TODO: handle lines that are longer than terminal width.
    // Should we wrap them or truncate them?
    pub fn redraw(&mut self, update: &Update) {
        unimplemented!();
        /*
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();

        let nb_lines = cmp::min(update.lines.len(), self.size.1 as usize);
        if nb_lines > 0 {
            for line in update.lines.iter().take(nb_lines - 1) {
                write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();

                if let Some(selection) = line.selection {
                    let start = selection.0 as usize;
                    let end = selection.1 as usize;
                    let mut str_before = String::new();
                    for c in line.text.chars().take(start) {
                        str_before.push(c);
                    }
                    let mut str_selection = String::new();
                    for c in line.text.chars().skip(start).take(end) {
                        str_selection.push(c);
                    }
                    let mut str_after = String::new();
                    for c in line.text.chars().skip(end) {
                        str_after.push(c);
                    }
                    write!(self.stdout, "{}{}{}{}{}{}",
                           termion::style::Reset,
                           str_before,
                           termion::color::Bg(color::Red),
                           str_selection,
                           termion::style::Reset,
                           str_after).unwrap();
                } else {
                    self.stdout.write_all(line.text.as_bytes()).unwrap();
                }
            }

            // If the last line has a trailing \n, we need to remove it
            let mut last_line = update.lines[nb_lines - 1].text.clone();
            match last_line.pop() {
                Some('\n') | None => {
                },
                Some(c) => {
                    last_line.push(c);
                }
            }
            write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
            self.stdout.write_all(last_line.as_bytes()).unwrap();
        }

        let cursor_line_idx = update.scroll_to.0 - update.first_line;
        let cursor_line = update.lines[cursor_line_idx as usize].text.clone();
        let mut cols =  0;
        for c in cursor_line.chars().take(update.scroll_to.1 as usize) {
            if c == '\t' {
                cols += 4;
            } else {
                cols += 1;
            }
        }
        self.scroll(cols + 1, cursor_line_idx + 1);
        */
    }

    pub fn draw(&mut self, view: &View) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();

        let range = 0..(cmp::min(view.lines.len(), self.size.1 as usize));
        for (lineno, line) in range.zip(view.lines.iter()) {
            write!(self.stdout, "{}", line.text.clone().unwrap()).unwrap();
            self.scroll(0, lineno as u64);
        }
    }

    pub fn scroll(&mut self, col: u64, line: u64) {
        write!(self.stdout, "{}", cursor::Goto((col + 1) as u16, (line + 1) as u16)).unwrap();
        self.stdout.flush().unwrap();
    }

    pub fn init(&mut self) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        self.stdout.flush().unwrap();
    }

    pub fn update(&mut self, core: &mut Core) {
        // TODO: check if terminal size changed. If so, send a `render_line` command to the backend,
        // and a `scroll` command for future updates.
        if let Ok(msg) = core.update_rx.try_recv() {
            let msg_list = msg.as_array().unwrap();
            let (method, params) = (msg_list[0].as_str().unwrap(), msg_list[1].as_object().unwrap());
            if method == "update" {
                let update = Update::from_value(params.get("update").unwrap());
                core.update(&update);

                let view = core.view();
                self.draw(&view);
            } else if method == "scroll_to" {
                let (col, line) = (params.get("col").unwrap().as_u64().unwrap(), params.get("line").unwrap().as_u64().unwrap());
                self.scroll(col, line);
            } else if method == "set_style" {
                // TODO: ???
            } else {
                info!("Unknown request from backend {:?}", method);
            }
        } else {
            thread::sleep(time::Duration::from_millis(10));
        }
    }
}
<|endoftext|>


<commit_before>use handlebars::Handlebars;
use rocket;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    rocket::ignite()
        .mount("/api",
               routes![api::all_fcps, api::member_fcps, api::github_webhook])
        .mount("/", routes![html::all_fcps, html::member_fcps])
        .launch();
}

mod html {
    use std::collections::BTreeMap;
    use rocket::response::content;
    use super::TEMPLATES;
    use error::DashResult;
    use nag;

    #[get("/")]
    fn all_fcps() -> DashResult<content::Html<String>> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                     json!({
                "team": team_label,
                "fcps": fcps,
            })
                 })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({ "model": context }))?;
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    fn member_fcps(username: String) -> DashResult<content::Html<String>> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use rocket_contrib::Json;
    use DB_POOL;
    use domain::github::GitHubUser;
    use error::DashResult;
    use github::{handle_comment, handle_issue, handle_pr};
    use github::webhooks::{Event, Payload};
    use nag;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(username: String)
                       -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data = "<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(conn,
                                 comment_event.issue,
                                 &comment_event.repository.full_name)?;
                    handle_comment(conn,
                                   comment_event.comment,
                                   &comment_event.repository.full_name)?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars.register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars.register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<commit_msg>logging rocket panics and rustfmt<commit_after>use std::panic::catch_unwind;
use handlebars::Handlebars;
use rocket;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    loop {
        let result = catch_unwind(|| {
            rocket::ignite()
                .mount(
                    "/api",
                    routes![api::all_fcps, api::member_fcps, api::github_webhook],
                )
                .mount("/", routes![html::all_fcps, html::member_fcps])
                .launch();
        });

        if let Err(why) = result {
            error!("Rocket failed to ignite: {:?}", why);
        }
    }
}

mod html {
    use std::collections::BTreeMap;
    use rocket::response::content;
    use super::TEMPLATES;
    use error::DashResult;
    use nag;

    #[get("/")]
    fn all_fcps() -> DashResult<content::Html<String>> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                json!({
                "team": team_label,
                "fcps": fcps,
            })
            })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({ "model": context }))?;
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    fn member_fcps(username: String) -> DashResult<content::Html<String>> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use rocket_contrib::Json;
    use DB_POOL;
    use domain::github::GitHubUser;
    use error::DashResult;
    use github::{handle_comment, handle_issue, handle_pr};
    use github::webhooks::{Event, Payload};
    use nag;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(
        username: String,
    ) -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data = "<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(
                        conn,
                        comment_event.issue,
                        &comment_event.repository.full_name,
                    )?;
                    handle_comment(
                        conn,
                        comment_event.comment,
                        &comment_event.repository.full_name,
                    )?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars.register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars.register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<|endoftext|>


<commit_before>use std::io::{Error, ErrorKind, Read, Write, Result};
use std::net::SocketAddr;
use std::sync::Arc;
use fibers::Spawn;
use fibers::net::{TcpListener, TcpStream};
use fibers::sync::oneshot::{Monitor, MonitorError};
use futures::{self, Future, IntoFuture, Poll, Stream, Async};
use futures::future::Either;
use handy_async::io::AsyncWrite;
use httparse;

use Method;
use {Header, Headers};
use headers::ContentLength;

pub struct HttpServerHandle {
    monitor: Monitor<(), Error>,
}
impl Future for HttpServerHandle {
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.monitor.poll().map_err(|e| {
            match e {
                MonitorError::Aborted => {
                    Error::new(ErrorKind::ConnectionAborted,
                               "The HTTP server exited unexpectedly")
                }
                MonitorError::Failed(e) => e,
            }
        })
    }
}
// impl stop,get_opts,set_opts,etc

pub trait HandleRequest: Clone + Send + 'static {
    type Future: Future<Item = ResponseBody, Error = ()> + Send;
    fn handle_request(self, req: Request) -> Self::Future;
    fn handle_error(self, server: SocketAddr, client: SocketAddr, error: Error) {
        error!("HTTP connection between {:?}(server) and {:?}(client) is disconnected: {}",
               client,
               server,
               error);

    }
}
impl<F, G> HandleRequest for Arc<Box<F>>
    where F: Fn(Request) -> G + Sync + Send + 'static,
          G: IntoFuture<Item = ResponseBody, Error = ()>,
          G::Future: Send
{
    type Future = G::Future;
    fn handle_request(self, req: Request) -> Self::Future {
        self(req).into_future()
    }
}

pub struct HttpServer<S> {
    addr: SocketAddr,
    spawner: S,
}
impl<S> HttpServer<S>
    where S: Spawn + Clone + Send + 'static
{
    pub fn new(addr: SocketAddr, spawner: S) -> Self {
        HttpServer {
            addr: addr,
            spawner: spawner,
        }
    }
    pub fn start_fn<F, G>(self, f: F) -> HttpServerHandle
        where F: Fn(Request) -> G + Sync + Send + 'static,
              G: IntoFuture<Item = ResponseBody, Error = ()>, // TODO: Error = Error
              G::Future: Send
    {
        self.start(Arc::new(Box::new(f)))
    }

    pub fn start<H>(self, handler: H) -> HttpServerHandle
        where H: HandleRequest
    {
        let HttpServer { addr, spawner } = self;
        let monitor = spawner.clone().spawn_monitor(TcpListener::bind(addr).and_then(|listener| {
            // TODO: support keep alive
            let server_addr = listener.local_addr().unwrap();
            listener.incoming().for_each(move |(client, client_addr)| {
                let handler = handler.clone();
                spawner.spawn(client.and_then(|socket| ReadHeader::new(socket))
                    .then(move |result| {
                        match result {
                            Err(e) => {
                                handler.handle_error(server_addr, client_addr, e);
                                Either::A(futures::failed(()))
                            }
                            Ok(req) => {
                                Either::B(handler.handle_request(req)
                                    .and_then(|res| {
                                        res.stream.async_flush().map(|_| ()).map_err(|_| ())
                                    }))
                            }
                        }
                    }));
                Ok(())
            })
        }));
        HttpServerHandle { monitor: monitor }
    }
}

pub struct ReadHeaderInner<'a> {
    reader: TcpStream,
    buf: Vec<u8>,
    offset: usize,
    headers: Vec<httparse::Header<'a>>,
}

pub struct ReadHeader<'a>(Option<ReadHeaderInner<'a>>);

impl<'a> ReadHeader<'a> {
    fn new(reader: TcpStream) -> Self {
        ReadHeader(Some(ReadHeaderInner {
            reader: reader,
            buf: vec![0; 1024],
            offset: 0,
            headers: vec![httparse::EMPTY_HEADER; 8],
        }))
    }
}
impl<'a> Future for ReadHeader<'a> {
    type Item = Request<'a>;
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut inner = self.0.take().expect("Cannot poll ReadHeader twice");
        if inner.offset == inner.buf.len() {
            let new_len = inner.offset * 2; // TODO: max size
            inner.buf.resize(new_len, 0);
        }
        match inner.reader.read(&mut inner.buf[inner.offset..]) {
            Err(e) => {
                if e.kind() == ErrorKind::WouldBlock {
                    self.0 = Some(inner);
                    Ok(Async::NotReady)
                } else {
                    Err(e)
                }
            }
            Ok(read_size) => {
                inner.offset += read_size;
                loop {
                    let result = {
                        let buf = &inner.buf[0..inner.offset];
                        let buf = unsafe { &*(buf as *const _) as &'static _ };
                        let mut req = httparse::Request::new(&mut inner.headers);
                        match req.parse(buf) {
                            Err(httparse::Error::TooManyHeaders) => Err(true),
                            Err(e) => {
                                let e = Error::new(ErrorKind::InvalidData,
                                                   format!("HTTP parse failure: {}", e));
                                return Err(e);
                            }
                            Ok(httparse::Status::Partial) => Err(false),
                            Ok(httparse::Status::Complete(body_offset)) => {
                                let result = (Method::from_str(req.method.unwrap()),
                                              req.path.unwrap(),
                                              req.version.unwrap(),
                                              req.headers.len(),
                                              body_offset);
                                Ok(result)
                            }
                        }
                    };
                    match result {
                        Ok((method, path, version, header_count, body_offset)) => {
                            inner.headers.truncate(header_count);
                            let body =
                                RequestBodyStream::new(inner.reader,
                                                       inner.buf,
                                                       body_offset,
                                                       &Headers { headers: &inner.headers })?;
                            let req = Request {
                                method: method,
                                path: path,
                                version: version,
                                headers: inner.headers,
                                body: body,
                            };
                            return Ok(Async::Ready(req));
                        }
                        Err(false) => {
                            self.0 = Some(inner);
                            return self.poll();
                        }
                        Err(true) => {
                            // retry
                            let new_len = inner.headers.len() * 2; // TODO: size limit
                            inner.headers.resize(new_len, httparse::EMPTY_HEADER);
                        }
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct RequestBodyStream<R: Read, B: AsRef<[u8]>> {
    stream: R,
    buf: B,
    offset: usize,
    remainings: u64,
}
impl<R: Read, B: AsRef<[u8]>> RequestBodyStream<R, B> {
    fn new(stream: R,
           buf: B,
           offset: usize,
           headers: &Headers)
           -> ::std::result::Result<Self, Error> {
        if headers.get_bytes("Transfer-Encoding").is_some() {
            unimplemented!()
        }
        let header = headers.get::<ContentLength>()?;
        Ok(RequestBodyStream {
            stream: stream,
            buf: buf,
            offset: offset,
            remainings: header.map(|h| h.len()).unwrap_or(0),
        })
    }
    fn unread_bytes(&self) -> Vec<u8> {
        Vec::from(&self.buf.as_ref()[self.offset..])
    }
}
impl<R: Read, B: AsRef<[u8]>> Read for RequestBodyStream<R, B> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        use std::cmp;
        let max_size = cmp::min(buf.len() as u64, self.remainings) as usize;
        if self.offset < self.buf.as_ref().len() {
            let size = cmp::min(max_size, self.buf.as_ref().len() - self.offset);
            buf[0..size].copy_from_slice(&self.buf.as_ref()[self.offset..self.offset + size]);
            self.offset += size;
            self.remainings -= size as u64;
            Ok(size)
        } else {
            let size = self.stream.read(&mut buf[0..max_size])?;
            self.remainings -= size as u64;
            Ok(size)
        }
    }
}

#[derive(Debug)]
pub struct Request<'a> {
    method: Method<'a>,
    path: &'a str,
    version: u8,
    headers: Vec<httparse::Header<'a>>,
    body: RequestBodyStream<TcpStream, Vec<u8>>,
}
impl<'a> Request<'a> {
    pub fn method(&self) -> &Method {
        &self.method
    }
    pub fn path(&self) -> &str {
        &self.path
    }
    pub fn version(&self) -> u8 {
        self.version
    }
    pub fn headers(&self) -> Headers {
        Headers { headers: &self.headers }
    }
    pub fn into_response(self, status_code: u16, status_reason: &'static str) -> Response {
        let unread = self.body.unread_bytes();
        let mut buf = Vec::with_capacity(1024);
        write!(buf,
               "HTTP/1.{} {} {}\r\n",
               self.version,
               status_code,
               status_reason)
            .unwrap();
        Response {
            pre_body_buf: buf,
            stream: self.body.stream,
            unread: unread,
        }
    }
}
impl<'a> Read for Request<'a> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.body.read(buf)
    }
}

pub struct Response {
    pre_body_buf: Vec<u8>,
    stream: TcpStream,
    unread: Vec<u8>,
}
impl Response {
    pub fn add_header<H: Header>(&mut self, header: &H) -> &mut Self {
        header.write(&mut self.pre_body_buf);
        self.pre_body_buf.extend_from_slice(b"\r\n");
        self
    }
    pub fn add_raw_header(&mut self, key: &str, value: &[u8]) {
        self.pre_body_buf.extend_from_slice(key.as_bytes());
        self.pre_body_buf.extend_from_slice(value);
        self.pre_body_buf.extend_from_slice(b"\r\n");
    }
    // TODO: into_body_stream
    pub fn into_body(mut self) -> ResponseBody {
        self.pre_body_buf.extend_from_slice(b"\r\n");
        ResponseBody {
            pre_body_buf: self.pre_body_buf,
            pre_body_offset: 0,
            stream: self.stream,
            unread: self.unread,
        }
    }
    pub fn write_body_bytes<B: AsRef<[u8]>>(self, body: B) -> WriteBodyBytes<B> {
        use handy_async::io::AsyncWrite;
        WriteBodyBytes(self.into_body().async_write_all(body))
    }
}

use handy_async::io::futures::WriteAll;
pub struct WriteBodyBytes<B>(WriteAll<ResponseBody, B>);
impl<B: AsRef<[u8]>> Future for WriteBodyBytes<B> {
    type Item = ResponseBody;
    type Error = ();
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(self.0.poll().map_err(|_| ())?.map(|(s, _)| s))
    }
}

pub struct ResponseBody {
    pre_body_buf: Vec<u8>,
    pre_body_offset: usize,
    stream: TcpStream,
    unread: Vec<u8>,
}
impl Write for ResponseBody {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        if self.pre_body_offset < self.pre_body_buf.len() {
            let size = self.stream.write(&self.pre_body_buf[self.pre_body_offset..])?;
            if size == 0 {
                Err(Error::new(ErrorKind::UnexpectedEof, "TODO"))
            } else {
                self.pre_body_offset += size;
                self.write(buf)
            }
        } else {
            self.stream.write(buf)
        }
    }
    fn flush(&mut self) -> Result<()> {
        if self.pre_body_offset < self.pre_body_buf.len() {
            let size = self.stream.write(&self.pre_body_buf[self.pre_body_offset..])?;
            if size == 0 {
                Err(Error::new(ErrorKind::UnexpectedEof, "TODO"))
            } else {
                self.pre_body_offset += size;
                self.flush()
            }
        } else {
            self.stream.flush()
        }
    }
}
<commit_msg>Add handling of unexpected eof in reading HTTP header<commit_after>use std::io::{Error, ErrorKind, Read, Write, Result};
use std::net::SocketAddr;
use std::sync::Arc;
use fibers::Spawn;
use fibers::net::{TcpListener, TcpStream};
use fibers::sync::oneshot::{Monitor, MonitorError};
use futures::{self, Future, IntoFuture, Poll, Stream, Async};
use futures::future::Either;
use handy_async::io::AsyncWrite;
use httparse;

use Method;
use {Header, Headers};
use headers::ContentLength;

pub struct HttpServerHandle {
    monitor: Monitor<(), Error>,
}
impl Future for HttpServerHandle {
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.monitor.poll().map_err(|e| {
            match e {
                MonitorError::Aborted => {
                    Error::new(ErrorKind::ConnectionAborted,
                               "The HTTP server exited unexpectedly")
                }
                MonitorError::Failed(e) => e,
            }
        })
    }
}
// impl stop,get_opts,set_opts,etc

pub trait HandleRequest: Clone + Send + 'static {
    type Future: Future<Item = ResponseBody, Error = ()> + Send;
    fn handle_request(self, req: Request) -> Self::Future;
    fn handle_error(self, server: SocketAddr, client: SocketAddr, error: Error) {
        error!("HTTP connection between {:?}(server) and {:?}(client) is disconnected: {}",
               client,
               server,
               error);

    }
}
impl<F, G> HandleRequest for Arc<Box<F>>
    where F: Fn(Request) -> G + Sync + Send + 'static,
          G: IntoFuture<Item = ResponseBody, Error = ()>,
          G::Future: Send
{
    type Future = G::Future;
    fn handle_request(self, req: Request) -> Self::Future {
        self(req).into_future()
    }
}

pub struct HttpServer<S> {
    addr: SocketAddr,
    spawner: S,
}
impl<S> HttpServer<S>
    where S: Spawn + Clone + Send + 'static
{
    pub fn new(addr: SocketAddr, spawner: S) -> Self {
        HttpServer {
            addr: addr,
            spawner: spawner,
        }
    }
    pub fn start_fn<F, G>(self, f: F) -> HttpServerHandle
        where F: Fn(Request) -> G + Sync + Send + 'static,
              G: IntoFuture<Item = ResponseBody, Error = ()>, // TODO: Error = Error
              G::Future: Send
    {
        self.start(Arc::new(Box::new(f)))
    }

    pub fn start<H>(self, handler: H) -> HttpServerHandle
        where H: HandleRequest
    {
        let HttpServer { addr, spawner } = self;
        let monitor = spawner.clone().spawn_monitor(TcpListener::bind(addr).and_then(|listener| {
            // TODO: support keep alive
            let server_addr = listener.local_addr().unwrap();
            listener.incoming().for_each(move |(client, client_addr)| {
                let handler = handler.clone();
                spawner.spawn(client.and_then(|socket| ReadHeader::new(socket))
                    .then(move |result| {
                        match result {
                            Err(e) => {
                                handler.handle_error(server_addr, client_addr, e);
                                Either::A(futures::failed(()))
                            }
                            Ok(req) => {
                                Either::B(handler.handle_request(req)
                                    .and_then(|res| {
                                        res.stream.async_flush().map(|_| ()).map_err(|_| ())
                                    }))
                            }
                        }
                    }));
                Ok(())
            })
        }));
        HttpServerHandle { monitor: monitor }
    }
}

pub struct ReadHeaderInner<'a> {
    reader: TcpStream,
    buf: Vec<u8>,
    offset: usize,
    headers: Vec<httparse::Header<'a>>,
}

pub struct ReadHeader<'a>(Option<ReadHeaderInner<'a>>);

impl<'a> ReadHeader<'a> {
    fn new(reader: TcpStream) -> Self {
        ReadHeader(Some(ReadHeaderInner {
            reader: reader,
            buf: vec![0; 1024],
            offset: 0,
            headers: vec![httparse::EMPTY_HEADER; 8],
        }))
    }
}
impl<'a> Future for ReadHeader<'a> {
    type Item = Request<'a>;
    type Error = Error;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut inner = self.0.take().expect("Cannot poll ReadHeader twice");
        if inner.offset == inner.buf.len() {
            let new_len = inner.offset * 2; // TODO: max size
            inner.buf.resize(new_len, 0);
        }
        match inner.reader.read(&mut inner.buf[inner.offset..]) {
            Err(e) => {
                if e.kind() == ErrorKind::WouldBlock {
                    self.0 = Some(inner);
                    Ok(Async::NotReady)
                } else {
                    Err(e)
                }
            }
            Ok(0) => Err(Error::new(ErrorKind::UnexpectedEof, "TODO")),
            Ok(read_size) => {
                inner.offset += read_size;
                loop {
                    let result = {
                        let buf = &inner.buf[0..inner.offset];
                        let buf = unsafe { &*(buf as *const _) as &'static _ };
                        let mut req = httparse::Request::new(&mut inner.headers);
                        match req.parse(buf) {
                            Err(httparse::Error::TooManyHeaders) => Err(true),
                            Err(e) => {
                                let e = Error::new(ErrorKind::InvalidData,
                                                   format!("HTTP parse failure: {}", e));
                                return Err(e);
                            }
                            Ok(httparse::Status::Partial) => Err(false),
                            Ok(httparse::Status::Complete(body_offset)) => {
                                let result = (Method::from_str(req.method.unwrap()),
                                              req.path.unwrap(),
                                              req.version.unwrap(),
                                              req.headers.len(),
                                              body_offset);
                                Ok(result)
                            }
                        }
                    };
                    match result {
                        Ok((method, path, version, header_count, body_offset)) => {
                            inner.headers.truncate(header_count);
                            let body =
                                RequestBodyStream::new(inner.reader,
                                                       inner.buf,
                                                       body_offset,
                                                       &Headers { headers: &inner.headers })?;
                            let req = Request {
                                method: method,
                                path: path,
                                version: version,
                                headers: inner.headers,
                                body: body,
                            };
                            return Ok(Async::Ready(req));
                        }
                        Err(false) => {
                            self.0 = Some(inner);
                            return self.poll();
                        }
                        Err(true) => {
                            // retry
                            let new_len = inner.headers.len() * 2; // TODO: size limit
                            inner.headers.resize(new_len, httparse::EMPTY_HEADER);
                        }
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct RequestBodyStream<R: Read, B: AsRef<[u8]>> {
    stream: R,
    buf: B,
    offset: usize,
    remainings: u64,
}
impl<R: Read, B: AsRef<[u8]>> RequestBodyStream<R, B> {
    fn new(stream: R,
           buf: B,
           offset: usize,
           headers: &Headers)
           -> ::std::result::Result<Self, Error> {
        if headers.get_bytes("Transfer-Encoding").is_some() {
            unimplemented!()
        }
        let header = headers.get::<ContentLength>()?;
        Ok(RequestBodyStream {
            stream: stream,
            buf: buf,
            offset: offset,
            remainings: header.map(|h| h.len()).unwrap_or(0),
        })
    }
    fn unread_bytes(&self) -> Vec<u8> {
        Vec::from(&self.buf.as_ref()[self.offset..])
    }
}
impl<R: Read, B: AsRef<[u8]>> Read for RequestBodyStream<R, B> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        use std::cmp;
        let max_size = cmp::min(buf.len() as u64, self.remainings) as usize;
        if self.offset < self.buf.as_ref().len() {
            let size = cmp::min(max_size, self.buf.as_ref().len() - self.offset);
            buf[0..size].copy_from_slice(&self.buf.as_ref()[self.offset..self.offset + size]);
            self.offset += size;
            self.remainings -= size as u64;
            Ok(size)
        } else {
            let size = self.stream.read(&mut buf[0..max_size])?;
            self.remainings -= size as u64;
            Ok(size)
        }
    }
}

#[derive(Debug)]
pub struct Request<'a> {
    method: Method<'a>,
    path: &'a str,
    version: u8,
    headers: Vec<httparse::Header<'a>>,
    body: RequestBodyStream<TcpStream, Vec<u8>>,
}
impl<'a> Request<'a> {
    pub fn method(&self) -> &Method {
        &self.method
    }
    pub fn path(&self) -> &str {
        &self.path
    }
    pub fn version(&self) -> u8 {
        self.version
    }
    pub fn headers(&self) -> Headers {
        Headers { headers: &self.headers }
    }
    pub fn into_response(self, status_code: u16, status_reason: &'static str) -> Response {
        let unread = self.body.unread_bytes();
        let mut buf = Vec::with_capacity(1024);
        write!(buf,
               "HTTP/1.{} {} {}\r\n",
               self.version,
               status_code,
               status_reason)
            .unwrap();
        Response {
            pre_body_buf: buf,
            stream: self.body.stream,
            unread: unread,
        }
    }
}
impl<'a> Read for Request<'a> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.body.read(buf)
    }
}

pub struct Response {
    pre_body_buf: Vec<u8>,
    stream: TcpStream,
    unread: Vec<u8>,
}
impl Response {
    pub fn add_header<H: Header>(&mut self, header: &H) -> &mut Self {
        header.write(&mut self.pre_body_buf);
        self.pre_body_buf.extend_from_slice(b"\r\n");
        self
    }
    pub fn add_raw_header(&mut self, key: &str, value: &[u8]) {
        self.pre_body_buf.extend_from_slice(key.as_bytes());
        self.pre_body_buf.extend_from_slice(value);
        self.pre_body_buf.extend_from_slice(b"\r\n");
    }
    // TODO: into_body_stream
    pub fn into_body(mut self) -> ResponseBody {
        self.pre_body_buf.extend_from_slice(b"\r\n");
        ResponseBody {
            pre_body_buf: self.pre_body_buf,
            pre_body_offset: 0,
            stream: self.stream,
            unread: self.unread,
        }
    }
    pub fn write_body_bytes<B: AsRef<[u8]>>(self, body: B) -> WriteBodyBytes<B> {
        use handy_async::io::AsyncWrite;
        WriteBodyBytes(self.into_body().async_write_all(body))
    }
}

use handy_async::io::futures::WriteAll;
pub struct WriteBodyBytes<B>(WriteAll<ResponseBody, B>);
impl<B: AsRef<[u8]>> Future for WriteBodyBytes<B> {
    type Item = ResponseBody;
    type Error = ();
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(self.0.poll().map_err(|_| ())?.map(|(s, _)| s))
    }
}

pub struct ResponseBody {
    pre_body_buf: Vec<u8>,
    pre_body_offset: usize,
    stream: TcpStream,
    unread: Vec<u8>,
}
impl Write for ResponseBody {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        if self.pre_body_offset < self.pre_body_buf.len() {
            let size = self.stream.write(&self.pre_body_buf[self.pre_body_offset..])?;
            if size == 0 {
                Err(Error::new(ErrorKind::UnexpectedEof, "TODO"))
            } else {
                self.pre_body_offset += size;
                self.write(buf)
            }
        } else {
            self.stream.write(buf)
        }
    }
    fn flush(&mut self) -> Result<()> {
        if self.pre_body_offset < self.pre_body_buf.len() {
            let size = self.stream.write(&self.pre_body_buf[self.pre_body_offset..])?;
            if size == 0 {
                Err(Error::new(ErrorKind::UnexpectedEof, "TODO"))
            } else {
                self.pre_body_offset += size;
                self.flush()
            }
        } else {
            self.stream.flush()
        }
    }
}
<|endoftext|>