<commit_before>
import str::sbuf;

export program;
export run_program;
export start_program;
export program_output;
export spawn_process;
export waitpid;

native "rust" mod rustrt {
    fn rust_run_program(argv: *sbuf, in_fd: int, out_fd: int, err_fd: int) ->
       int;
}

fn arg_vec(prog: str, args: [@str]) -> [sbuf] {
    let argptrs = str::as_buf(prog, {|buf| [buf] });
    for arg in args { argptrs += str::as_buf(*arg, {|buf| [buf] }); }
    argptrs += [unsafe::reinterpret_cast(0)];
    ret argptrs;
}

fn spawn_process(prog: str, args: [str], in_fd: int, out_fd: int, err_fd: int)
   -> int unsafe {
    // Note: we have to hold on to these vector references while we hold a
    // pointer to their buffers
    let prog = prog;
    let args = vec::map({|arg| @arg }, args);
    let argv = arg_vec(prog, args);
    let pid =
        rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd,
                                 err_fd);
    ret pid;
}

fn run_program(prog: str, args: [str]) -> int {
    ret waitpid(spawn_process(prog, args, 0, 0, 0));
}

type program =
    obj {
        fn get_id() -> int;
        fn input() -> io::writer;
        fn output() -> io::reader;
        fn err() -> io::reader;
        fn close_input();
        fn finish() -> int;
        fn destroy();
    };

resource program_res(p: program) { p.destroy(); }

fn start_program(prog: str, args: [str]) -> @program_res {
    let pipe_input = os::pipe();
    let pipe_output = os::pipe();
    let pipe_err = os::pipe();
    let pid =
        spawn_process(prog, args, pipe_input.in, pipe_output.out,
                      pipe_err.out);

    if pid == -1 { fail; }
    os::libc::close(pipe_input.in);
    os::libc::close(pipe_output.out);
    os::libc::close(pipe_err.out);
    obj new_program(pid: int,
                    mutable in_fd: int,
                    out_file: os::libc::FILE,
                    err_file: os::libc::FILE,
                    mutable finished: bool) {
        fn get_id() -> int { ret pid; }
        fn input() -> io::writer {
            ret io::new_writer(io::fd_buf_writer(in_fd, option::none));
        }
        fn output() -> io::reader {
            ret io::new_reader(io::FILE_buf_reader(out_file, option::none));
        }
        fn err() -> io::reader {
            ret io::new_reader(io::FILE_buf_reader(err_file, option::none));
        }
        fn close_input() {
            let invalid_fd = -1;
            if in_fd != invalid_fd {
                os::libc::close(in_fd);
                in_fd = invalid_fd;
            }
        }
        fn finish() -> int {
            if finished { ret 0; }
            finished = true;
            self.close_input();
            ret waitpid(pid);
        }
        fn destroy() {
            self.finish();
            os::libc::fclose(out_file);
            os::libc::fclose(err_file);
        }
    }
    ret @program_res(new_program(pid, pipe_input.out,
                                 os::fd_FILE(pipe_output.in),
                                 os::fd_FILE(pipe_err.in), false));
}

fn read_all(rd: io::reader) -> str {
    let buf = "";
    while !rd.eof() {
        let bytes = rd.read_bytes(4096u);
        buf += str::unsafe_from_bytes(bytes);
    }
    ret buf;
}

fn program_output(prog: str, args: [str]) ->
   {status: int, out: str, err: str} {
    let pr = start_program(prog, args);
    pr.close_input();
    let out = read_all(pr.output());
    let err = read_all(pr.err());
    ret {status: pr.finish(), out: out, err: err};
}

/* Returns an exit status */
#[cfg(target_os = "win32")]
fn waitpid(pid: int) -> int {
    os::waitpid(pid)
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
fn waitpid(pid: int) -> int {
    #[cfg(target_os = "linux")]
    fn WIFEXITED(status: int) -> bool {
        (status & 0xff) == 0
    }

    #[cfg(target_os = "macos")]
    fn WIFEXITED(status: int) -> bool {
        (status & 0x7f) == 0
    }

    #[cfg(target_os = "linux")]
    fn WEXITSTATUS(status: int) -> int {
        (status >> 8) & 0xff
    }

    #[cfg(target_os = "macos")]
    fn WEXITSTATUS(status: int) -> int {
        status >> 8
    }

    let status = os::waitpid(pid);
    ret if WIFEXITED(status) {
        WEXITSTATUS(status)
    } else {
        1
    };
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>migrate rust_run_program<commit_after>
import str::sbuf;

export program;
export run_program;
export start_program;
export program_output;
export spawn_process;
export waitpid;

native "c-stack-cdecl" mod rustrt {
    fn rust_run_program(argv: *sbuf, in_fd: int, out_fd: int, err_fd: int) ->
       int;
}

fn arg_vec(prog: str, args: [@str]) -> [sbuf] {
    let argptrs = str::as_buf(prog, {|buf| [buf] });
    for arg in args { argptrs += str::as_buf(*arg, {|buf| [buf] }); }
    argptrs += [unsafe::reinterpret_cast(0)];
    ret argptrs;
}

fn spawn_process(prog: str, args: [str], in_fd: int, out_fd: int, err_fd: int)
   -> int unsafe {
    // Note: we have to hold on to these vector references while we hold a
    // pointer to their buffers
    let prog = prog;
    let args = vec::map({|arg| @arg }, args);
    let argv = arg_vec(prog, args);
    let pid =
        rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd,
                                 err_fd);
    ret pid;
}

fn run_program(prog: str, args: [str]) -> int {
    ret waitpid(spawn_process(prog, args, 0, 0, 0));
}

type program =
    obj {
        fn get_id() -> int;
        fn input() -> io::writer;
        fn output() -> io::reader;
        fn err() -> io::reader;
        fn close_input();
        fn finish() -> int;
        fn destroy();
    };

resource program_res(p: program) { p.destroy(); }

fn start_program(prog: str, args: [str]) -> @program_res {
    let pipe_input = os::pipe();
    let pipe_output = os::pipe();
    let pipe_err = os::pipe();
    let pid =
        spawn_process(prog, args, pipe_input.in, pipe_output.out,
                      pipe_err.out);

    if pid == -1 { fail; }
    os::libc::close(pipe_input.in);
    os::libc::close(pipe_output.out);
    os::libc::close(pipe_err.out);
    obj new_program(pid: int,
                    mutable in_fd: int,
                    out_file: os::libc::FILE,
                    err_file: os::libc::FILE,
                    mutable finished: bool) {
        fn get_id() -> int { ret pid; }
        fn input() -> io::writer {
            ret io::new_writer(io::fd_buf_writer(in_fd, option::none));
        }
        fn output() -> io::reader {
            ret io::new_reader(io::FILE_buf_reader(out_file, option::none));
        }
        fn err() -> io::reader {
            ret io::new_reader(io::FILE_buf_reader(err_file, option::none));
        }
        fn close_input() {
            let invalid_fd = -1;
            if in_fd != invalid_fd {
                os::libc::close(in_fd);
                in_fd = invalid_fd;
            }
        }
        fn finish() -> int {
            if finished { ret 0; }
            finished = true;
            self.close_input();
            ret waitpid(pid);
        }
        fn destroy() {
            self.finish();
            os::libc::fclose(out_file);
            os::libc::fclose(err_file);
        }
    }
    ret @program_res(new_program(pid, pipe_input.out,
                                 os::fd_FILE(pipe_output.in),
                                 os::fd_FILE(pipe_err.in), false));
}

fn read_all(rd: io::reader) -> str {
    let buf = "";
    while !rd.eof() {
        let bytes = rd.read_bytes(4096u);
        buf += str::unsafe_from_bytes(bytes);
    }
    ret buf;
}

fn program_output(prog: str, args: [str]) ->
   {status: int, out: str, err: str} {
    let pr = start_program(prog, args);
    pr.close_input();
    let out = read_all(pr.output());
    let err = read_all(pr.err());
    ret {status: pr.finish(), out: out, err: err};
}

/* Returns an exit status */
#[cfg(target_os = "win32")]
fn waitpid(pid: int) -> int {
    os::waitpid(pid)
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
fn waitpid(pid: int) -> int {
    #[cfg(target_os = "linux")]
    fn WIFEXITED(status: int) -> bool {
        (status & 0xff) == 0
    }

    #[cfg(target_os = "macos")]
    fn WIFEXITED(status: int) -> bool {
        (status & 0x7f) == 0
    }

    #[cfg(target_os = "linux")]
    fn WEXITSTATUS(status: int) -> int {
        (status >> 8) & 0xff
    }

    #[cfg(target_os = "macos")]
    fn WEXITSTATUS(status: int) -> int {
        status >> 8
    }

    let status = os::waitpid(pid);
    ret if WIFEXITED(status) {
        WEXITSTATUS(status)
    } else {
        1
    };
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>
//! Glyph caching

use error::Error;
use freetype::ffi;
use freetype;
use freetype::error::Error::MissingFontField;
use Texture;
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};

pub type FontSize = u32;

/// Struct used to hold rendered character data.
pub struct Character {
    /// The glyph.
    pub glyph: freetype::Glyph,
    /// The bitmap of the character.
    pub bitmap_glyph: freetype::BitmapGlyph,
    /// The texture of the character.
    pub texture: Texture,
}

impl Character {
    /// The top offset.
    pub fn top(&self) -> i32 {
        self.bitmap_glyph.top()
    }

    /// The left offset.
    pub fn left(&self) -> i32 {
        self.bitmap_glyph.left()
    }
}

/// A struct used for caching rendered font.
pub struct GlyphCache {
    /// The font face.
    pub face: freetype::Face,
    data: HashMap<FontSize, HashMap<char, Character>>,
}

impl GlyphCache {

    /// Constructor for a GlyphCache.
    pub fn new(font: &Path) -> Result<GlyphCache, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        let font_str = match font.as_str() {
            Some(font_str) => font_str,
            None => return Err(Error::FreetypeError(MissingFontField)),
        };
        let face = match freetype.new_face(font_str, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
        })
    }

    /// Return a reference to a `Character`. If there is not yet a `Character` for
    /// the given `FontSize` and `char`, load the `Character`.
    pub fn get_character(&mut self, size: FontSize, ch: char) -> &Character {
        match {
            match self.data.entry(size) {
                Vacant(entry) => entry.set(HashMap::new()),
                Occupied(entry) => entry.into_mut(),
            }
        }.contains_key(&ch) {
            true => &self.data[size][ch],
            false => { self.load_character(size, ch); &self.data[size][ch] }
        }
    }

    /// Load a `Character` from a given `FontSize` and `char`.
    fn load_character(&mut self, size: FontSize, ch: char) {
        self.face.set_pixel_sizes(0, size).unwrap();
        self.face.load_char(ch as ffi::FT_ULong, freetype::face::DEFAULT).unwrap();
        let glyph = self.face.glyph().get_glyph().unwrap();
        let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None)
            .unwrap();
        let bitmap = bitmap_glyph.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        self.data[size].insert(ch, Character {
            glyph: glyph,
            bitmap_glyph: bitmap_glyph,
            texture: texture,
        });
    }

}
<commit_msg>Added `advance` method to `Character`<commit_after>
//! Glyph caching

use error::Error;
use freetype::ffi;
use freetype;
use freetype::error::Error::MissingFontField;
use Texture;
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};

pub type FontSize = u32;

/// Struct used to hold rendered character data.
pub struct Character {
    /// The glyph.
    pub glyph: freetype::Glyph,
    /// The bitmap of the character.
    pub bitmap_glyph: freetype::BitmapGlyph,
    /// The texture of the character.
    pub texture: Texture,
}

impl Character {
    /// The top offset.
    pub fn top(&self) -> i32 {
        self.bitmap_glyph.top()
    }

    /// The left offset.
    pub fn left(&self) -> i32 {
        self.bitmap_glyph.left()
    }

    /// Advances to next character position.
    pub fn advance(&self) -> freetype::Vector {
        self.glyph.advance()
    }
}

/// A struct used for caching rendered font.
pub struct GlyphCache {
    /// The font face.
    pub face: freetype::Face,
    data: HashMap<FontSize, HashMap<char, Character>>,
}

impl GlyphCache {

    /// Constructor for a GlyphCache.
    pub fn new(font: &Path) -> Result<GlyphCache, Error> {
        let freetype = match freetype::Library::init() {
            Ok(freetype) => freetype,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        let font_str = match font.as_str() {
            Some(font_str) => font_str,
            None => return Err(Error::FreetypeError(MissingFontField)),
        };
        let face = match freetype.new_face(font_str, 0) {
            Ok(face) => face,
            Err(why) => return Err(Error::FreetypeError(why)),
        };
        Ok(GlyphCache {
            face: face,
            data: HashMap::new(),
        })
    }

    /// Return a reference to a `Character`. If there is not yet a `Character` for
    /// the given `FontSize` and `char`, load the `Character`.
    pub fn get_character(&mut self, size: FontSize, ch: char) -> &Character {
        match {
            match self.data.entry(size) {
                Vacant(entry) => entry.set(HashMap::new()),
                Occupied(entry) => entry.into_mut(),
            }
        }.contains_key(&ch) {
            true => &self.data[size][ch],
            false => { self.load_character(size, ch); &self.data[size][ch] }
        }
    }

    /// Load a `Character` from a given `FontSize` and `char`.
    fn load_character(&mut self, size: FontSize, ch: char) {
        self.face.set_pixel_sizes(0, size).unwrap();
        self.face.load_char(ch as ffi::FT_ULong, freetype::face::DEFAULT).unwrap();
        let glyph = self.face.glyph().get_glyph().unwrap();
        let bitmap_glyph = glyph.to_bitmap(freetype::render_mode::RenderMode::Normal, None)
            .unwrap();
        let bitmap = bitmap_glyph.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        self.data[size].insert(ch, Character {
            glyph: glyph,
            bitmap_glyph: bitmap_glyph,
            texture: texture,
        });
    }

}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use prelude::v1::*;

use env;
use net::{SocketAddr, IpAddr};
use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

pub fn next_test_ip4() -> SocketAddr {
    static PORT: AtomicUsize = ATOMIC_USIZE_INIT;
    SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1),
                    PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
}

pub fn next_test_ip6() -> SocketAddr {
    static PORT: AtomicUsize = ATOMIC_USIZE_INIT;
    SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1),
                    PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
}

// The bots run multiple builds at the same time, and these builds
// all want to use ports. This function figures out which workspace
// it is running in and assigns a port range based on it.
fn base_port() -> u16 {
    let cwd = env::current_dir().unwrap();
    let dirs = ["32-opt", "32-nopt", "64-opt", "64-nopt", "64-opt-vg",
                "all-opt", "snap3", "dist"];
    dirs.iter().enumerate().find(|&(_, dir)| {
        cwd.to_str().unwrap().contains(dir)
    }).map(|p| p.0).unwrap_or(0) as u16 * 1000 + 19600
}
<commit_msg>Auto merge of #22857 - alexcrichton:net-flaky, r=alexcrichton<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use prelude::v1::*;

use env;
use net::{SocketAddr, IpAddr};
use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

static PORT: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn next_test_ip4() -> SocketAddr {
    SocketAddr::new(IpAddr::new_v4(127, 0, 0, 1),
                    PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
}

pub fn next_test_ip6() -> SocketAddr {
    SocketAddr::new(IpAddr::new_v6(0, 0, 0, 0, 0, 0, 0, 1),
                    PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port())
}

// The bots run multiple builds at the same time, and these builds
// all want to use ports. This function figures out which workspace
// it is running in and assigns a port range based on it.
fn base_port() -> u16 {
    let cwd = env::current_dir().unwrap();
    let dirs = ["32-opt", "32-nopt", "64-opt", "64-nopt", "64-opt-vg",
                "all-opt", "snap3", "dist"];
    dirs.iter().enumerate().find(|&(_, dir)| {
        cwd.to_str().unwrap().contains(dir)
    }).map(|p| p.0).unwrap_or(0) as u16 * 1000 + 19600
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The `ToBytes` and `IterBytes` traits

*/

use cast;
use container::Container;
use io;
use io::Writer;
use iterator::Iterator;
use option::{None, Option, Some};
use str::{Str, StrSlice};
use vec::{Vector, ImmutableVector};

pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool;

///
/// A trait to implement in order to make a type hashable;
/// This works in combination with the trait `std::hash::Hash`, and
/// may in the future be merged with that trait or otherwise
/// modified when default methods and trait inheritance are
/// completed.
///
/// IterBytes should be implemented so that the extent of the
/// produced byte stream can be discovered, given the original
/// type.
/// For example, the IterBytes implementation for vectors emits
/// its length first, and enums should emit their discriminant.
///
pub trait IterBytes {
    /// Call the provided callback `f` one or more times with
    /// byte-slices that should be used when computing a hash
    /// value or otherwise "flattening" the structure into
    /// a sequence of bytes. The `lsb0` parameter conveys
    /// whether the caller is asking for little-endian bytes
    /// (`true`) or big-endian (`false`); this should only be
    /// relevant in implementations that represent a single
    /// multi-byte datum such as a 32 bit integer or 64 bit
    /// floating-point value. It can be safely ignored for
    /// larger structured types as they are usually processed
    /// left-to-right in declaration order, regardless of
    /// underlying memory endianness.
    ///
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool;
}

impl IterBytes for bool {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        f([
            *self as u8
        ])
    }
}

impl IterBytes for u8 {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        f([
            *self
        ])
    }
}

impl IterBytes for u16 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8
            ])
        } else {
            f([
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for u32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8,
                (*self >> 16) as u8,
                (*self >> 24) as u8,
            ])
        } else {
            f([
                (*self >> 24) as u8,
                (*self >> 16) as u8,
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for u64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8,
                (*self >> 16) as u8,
                (*self >> 24) as u8,
                (*self >> 32) as u8,
                (*self >> 40) as u8,
                (*self >> 48) as u8,
                (*self >> 56) as u8
            ])
        } else {
            f([
                (*self >> 56) as u8,
                (*self >> 48) as u8,
                (*self >> 40) as u8,
                (*self >> 32) as u8,
                (*self >> 24) as u8,
                (*self >> 16) as u8,
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for i8 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u8).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i16 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u16).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for char {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

#[cfg(target_word_size = "32")]
impl IterBytes for uint {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

#[cfg(target_word_size = "64")]
impl IterBytes for uint {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for int {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

impl IterBytes for float {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as f64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for f32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        let i: u32 = unsafe {
            // 0.0 == -0.0 so they should also have the same hashcode
            cast::transmute(if *self == -0.0 { 0.0 } else { *self })
        };
        i.iter_bytes(lsb0, f)
    }
}

impl IterBytes for f64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        let i: u64 = unsafe {
            // 0.0 == -0.0 so they should also have the same hashcode
            cast::transmute(if *self == -0.0 { 0.0 } else { *self })
        };
        i.iter_bytes(lsb0, f)
    }
}

impl<'self,A:IterBytes> IterBytes for &'self [A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.len().iter_bytes(lsb0, |b| f(b)) &&
        self.iter().advance(|elt| elt.iter_bytes(lsb0, |b| f(b)))
    }
}

impl<A: IterBytes> IterBytes for (A, ) {
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        match *self {
            (ref a, ) => a.iter_bytes(lsb0, |b| f(b))
        }
    }
}

macro_rules! iter_bytes_tuple(
    ($($A:ident),+) => (
        impl<$($A: IterBytes),+> IterBytes for ($($A),+) {
            fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
                match *self {
                    ($(ref $A),+) => {
                        $(
                            $A .iter_bytes(lsb0, |b| f(b))
                        )&&+
                    }
                }
            }
        }
    )
)

iter_bytes_tuple!(A, B)
iter_bytes_tuple!(A, B, C)
iter_bytes_tuple!(A, B, C, D)
iter_bytes_tuple!(A, B, C, D, E)
iter_bytes_tuple!(A, B, C, D, E, F)
iter_bytes_tuple!(A, B, C, D, E, F, G)
iter_bytes_tuple!(A, B, C, D, E, F, G, H)

impl<A:IterBytes> IterBytes for ~[A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @[A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<'self> IterBytes for &'self str {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        // Terminate the string with a byte that does not appear in UTF-8
        f(self.as_bytes()) && f([0xFF])
    }
}

impl IterBytes for ~str {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl IterBytes for @str {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for Option<A> {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        match *self {
          Some(ref a) => 0u8.iter_bytes(lsb0, |b| f(b)) && a.iter_bytes(lsb0, |b| f(b)),
          None => 1u8.iter_bytes(lsb0, f)
        }
    }
}

impl<'self,A:IterBytes> IterBytes for &'self A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @mut A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for ~A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

// NB: raw-pointer IterBytes does _not_ dereference
// to the target; it just gives you the pointer-bytes.
impl<A> IterBytes for *A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

impl<A> IterBytes for *mut A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

/// A trait for converting a value to a list of bytes.
pub trait ToBytes {
    /// Converts the current value to a list of bytes. This is equivalent to
    /// invoking iter_bytes on a type and collecting all yielded values in an
    /// array
    fn to_bytes(&self, lsb0: bool) -> ~[u8];
}

impl<A:IterBytes> ToBytes for A {
    fn to_bytes(&self, lsb0: bool) -> ~[u8] {
        do io::with_bytes_writer |wr| {
            do self.iter_bytes(lsb0) |bytes| {
                wr.write(bytes);
                true
            };
        }
    }
}
<commit_msg>added IterBytes for 4-tuples<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The `ToBytes` and `IterBytes` traits

*/

use cast;
use container::Container;
use io;
use io::Writer;
use iterator::Iterator;
use option::{None, Option, Some};
use str::{Str, StrSlice};
use vec::{Vector, ImmutableVector};

pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool;

///
/// A trait to implement in order to make a type hashable;
/// This works in combination with the trait `std::hash::Hash`, and
/// may in the future be merged with that trait or otherwise
/// modified when default methods and trait inheritance are
/// completed.
///
/// IterBytes should be implemented so that the extent of the
/// produced byte stream can be discovered, given the original
/// type.
/// For example, the IterBytes implementation for vectors emits
/// its length first, and enums should emit their discriminant.
///
pub trait IterBytes {
    /// Call the provided callback `f` one or more times with
    /// byte-slices that should be used when computing a hash
    /// value or otherwise "flattening" the structure into
    /// a sequence of bytes. The `lsb0` parameter conveys
    /// whether the caller is asking for little-endian bytes
    /// (`true`) or big-endian (`false`); this should only be
    /// relevant in implementations that represent a single
    /// multi-byte datum such as a 32 bit integer or 64 bit
    /// floating-point value. It can be safely ignored for
    /// larger structured types as they are usually processed
    /// left-to-right in declaration order, regardless of
    /// underlying memory endianness.
    ///
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool;
}

impl IterBytes for bool {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        f([
            *self as u8
        ])
    }
}

impl IterBytes for u8 {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        f([
            *self
        ])
    }
}

impl IterBytes for u16 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8
            ])
        } else {
            f([
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for u32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8,
                (*self >> 16) as u8,
                (*self >> 24) as u8,
            ])
        } else {
            f([
                (*self >> 24) as u8,
                (*self >> 16) as u8,
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for u64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        if lsb0 {
            f([
                *self as u8,
                (*self >> 8) as u8,
                (*self >> 16) as u8,
                (*self >> 24) as u8,
                (*self >> 32) as u8,
                (*self >> 40) as u8,
                (*self >> 48) as u8,
                (*self >> 56) as u8
            ])
        } else {
            f([
                (*self >> 56) as u8,
                (*self >> 48) as u8,
                (*self >> 40) as u8,
                (*self >> 32) as u8,
                (*self >> 24) as u8,
                (*self >> 16) as u8,
                (*self >> 8) as u8,
                *self as u8
            ])
        }
    }
}

impl IterBytes for i8 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u8).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i16 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u16).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

impl IterBytes for i64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for char {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

#[cfg(target_word_size = "32")]
impl IterBytes for uint {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u32).iter_bytes(lsb0, f)
    }
}

#[cfg(target_word_size = "64")]
impl IterBytes for uint {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as u64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for int {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

impl IterBytes for float {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as f64).iter_bytes(lsb0, f)
    }
}

impl IterBytes for f32 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        let i: u32 = unsafe {
            // 0.0 == -0.0 so they should also have the same hashcode
            cast::transmute(if *self == -0.0 { 0.0 } else { *self })
        };
        i.iter_bytes(lsb0, f)
    }
}

impl IterBytes for f64 {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        let i: u64 = unsafe {
            // 0.0 == -0.0 so they should also have the same hashcode
            cast::transmute(if *self == -0.0 { 0.0 } else { *self })
        };
        i.iter_bytes(lsb0, f)
    }
}

impl<'self,A:IterBytes> IterBytes for &'self [A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.len().iter_bytes(lsb0, |b| f(b)) &&
        self.iter().advance(|elt| elt.iter_bytes(lsb0, |b| f(b)))
    }
}

impl<A: IterBytes> IterBytes for (A, ) {
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        match *self {
            (ref a, ) => a.iter_bytes(lsb0, |b| f(b))
        }
    }
}

macro_rules! iter_bytes_tuple(
    ($($A:ident),+) => (
        impl<$($A: IterBytes),+> IterBytes for ($($A),+) {
            fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
                match *self {
                    ($(ref $A),+) => {
                        $(
                            $A .iter_bytes(lsb0, |b| f(b))
                        )&&+
                    }
                }
            }
        }
    )
)

iter_bytes_tuple!(A, B)
iter_bytes_tuple!(A, B, C)
iter_bytes_tuple!(A, B, C, D)
iter_bytes_tuple!(A, B, C, D, E)
iter_bytes_tuple!(A, B, C, D, E, F)
iter_bytes_tuple!(A, B, C, D, E, F, G)
iter_bytes_tuple!(A, B, C, D, E, F, G, H)

impl<A:IterBytes> IterBytes for ~[A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @[A] {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<'self> IterBytes for &'self str {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: Cb) -> bool {
        // Terminate the string with a byte that does not appear in UTF-8
        f(self.as_bytes()) && f([0xFF])
    }
}

impl IterBytes for ~str {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl IterBytes for @str {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        self.as_slice().iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for Option<A> {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        match *self {
          Some(ref a) => 0u8.iter_bytes(lsb0, |b| f(b)) && a.iter_bytes(lsb0, |b| f(b)),
          None => 1u8.iter_bytes(lsb0, f)
        }
    }
}

impl<'self,A:IterBytes> IterBytes for &'self A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for @mut A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

impl<A:IterBytes> IterBytes for ~A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (**self).iter_bytes(lsb0, f)
    }
}

// NB: raw-pointer IterBytes does _not_ dereference
// to the target; it just gives you the pointer-bytes.
impl<A> IterBytes for *A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

impl<A> IterBytes for *mut A {
    #[inline]
    fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
        (*self as uint).iter_bytes(lsb0, f)
    }
}

/// A trait for converting a value to a list of bytes.
pub trait ToBytes {
    /// Converts the current value to a list of bytes. This is equivalent to
    /// invoking iter_bytes on a type and collecting all yielded values in an
    /// array
    fn to_bytes(&self, lsb0: bool) -> ~[u8];
}

impl<A:IterBytes> ToBytes for A {
    fn to_bytes(&self, lsb0: bool) -> ~[u8] {
        do io::with_bytes_writer |wr| {
            do self.iter_bytes(lsb0) |bytes| {
                wr.write(bytes);
                true
            };
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    // just test to see if it compiles:
    #[test] fn iterbytes_compiles () {
        takes_iterbytes((3,4,5,false));
    }
    fn takes_iterbytes<T : IterBytes>(x : T) {}
}
<|endoftext|>


<commit_before>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;

use entities::Faction;
use astronomicals::sector::Sector;
use astronomicals::system::System;
use tui::widgets::{Block, Borders, Row, Table, Widget};
use tui::widgets::canvas::Canvas;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Style};

/// Level of map display.
enum Level {
    Galaxy,
    Sector,
    System,
}

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_STYLES: HashMap<Faction, Style> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Style::default().fg(Color::Red));
        m.insert(Faction::Federation, Style::default().fg(Color::Yellow));
        m.insert(Faction::Cartel, Style::default().fg(Color::Magenta));
        m.insert(Faction::Independent, Style::default().fg(Color::LightGreen));
        m
    };

    /// Styling for selected item.
    static ref SELECTED_STYLE: Style = Style::default().bg(Color::Gray);

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    level: Level,
    selected_sector: usize,
    max_selected_sector: usize,
    selected_system: usize,
    max_selected_system: usize,
    selected_astronomical: usize,
    max_selected_astronomical: usize,
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>) -> Box<Self> {
        let max_selected_sector = state.galaxy.lock().unwrap().sectors.len();

        Box::new(MapTab {
            state: state,
            level: Level::Galaxy,
            selected_sector: 0,
            max_selected_sector: max_selected_sector,
            selected_system: 0,
            max_selected_system: 0,
            selected_astronomical: 0,
            max_selected_astronomical: 0,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => match input {
                // Move up item list.
                keyevent::Key::Char('k') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector = (self.selected_sector as i32 - 1).max(0) as usize
                    }
                    Level::Sector => {
                        self.selected_system = (self.selected_system as i32 - 1).max(0) as usize
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical as i32 - 1).max(0) as usize
                    }
                },

                // Move down item list.
                keyevent::Key::Char('j') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector =
                            (self.selected_sector + 1).min(self.max_selected_sector)
                    }
                    Level::Sector => {
                        self.selected_system =
                            (self.selected_system + 1).min(self.max_selected_system)
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical + 1).min(self.max_selected_astronomical)
                    }
                },

                // Move into item.
                keyevent::Key::Char('l') => {
                    self.level = match self.level {
                        Level::Galaxy => {
                            self.max_selected_system = self.state.galaxy.lock().unwrap().sectors
                                [self.selected_sector]
                                .systems
                                .len();
                            Level::Sector
                        }
                        Level::Sector => {
                            self.max_selected_astronomical =
                                self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                                    .systems[self.selected_system]
                                    .satelites
                                    .len();
                            Level::System
                        }
                        _ => Level::Galaxy,
                    };
                }

                // Move out of item.
                keyevent::Key::Char('h') => {
                    self.level = match self.level {
                        Level::System => Level::Sector,
                        Level::Sector => Level::Galaxy,
                        _ => Level::Galaxy,
                    };
                }
                _ => {}
            },
            _ => {}
        };
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Percent(30), Size::Percent(70)])
            .render(term, area, |term, chunks| match self.level {
                Level::Galaxy => {
                    let galaxy = self.state.galaxy.lock().unwrap();
                    draw_galaxy_table(self.selected_sector, &galaxy.sectors, term, &chunks[0]);
                    draw_galaxy_map(self.selected_sector, &galaxy.sectors, term, &chunks[1]);
                }
                Level::Sector => {
                    let sector = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector];
                    draw_sector_table(self.selected_system, &sector, term, &chunks[0]);
                    draw_sector_map(self.selected_system, &sector, term, &chunks[1]);
                }
                Level::System => {
                    let system = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                        .systems[self.selected_system];
                    draw_system_table(self.selected_astronomical, &system, term, &chunks[0]);
                    draw_system_map(self.selected_astronomical, &system, term, &chunks[1]);
                }
            });
    }
}

fn draw_galaxy_table(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Sector", "Faction", "Systems"].into_iter(),
        sectors
            .iter()
            .enumerate()
            .map(|(idx, ref sector)| {
                let style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => FACTION_STYLES.get(&sector.faction).unwrap(),
                };
                Row::StyledData(
                    vec![
                        sector.name.clone(),
                        sector.faction.to_string(),
                        (sector.systems.len() as u32).to_string(),
                    ].into_iter(),
                    style,
                )
            })
            .skip(selected),
    ).block(Block::default().title("Sectors").borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_galaxy_map(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let (max_x, max_y) = sectors.iter().fold((0., 0.), |(x_max, y_max), s| {
        let location = s.center();
        (
            location.coords.x.abs().max(x_max),
            location.coords.y.abs().max(y_max),
        )
    });
    Canvas::default()
        .block(Block::default().title("Galaxy").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, sector) in sectors.iter().enumerate() {
                let color = match sector.faction {
                    Faction::Empire => Color::Red,
                    Faction::Federation => Color::Yellow,
                    Faction::Cartel => Color::Magenta,
                    Faction::Independent => Color::LightGreen,
                };
                let center = sector.center();
                match idx == selected {
                    true => ctx.print(center.coords.x, center.coords.y, "X", color),
                    false => ctx.print(center.coords.x, center.coords.y, "*", color),
                }
            }
        })
        .x_bounds([-max_x, max_x])
        .y_bounds([-max_y, max_y])
        .render(term, area);
}

fn draw_sector_table(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["System", "Bodies"].into_iter(),
        sector
            .systems
            .iter()
            .enumerate()
            .map(|(idx, ref system)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        system.name.clone(),
                        (system.satelites.len() as u32).to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&sector.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_sector_map(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let sector_center = sector.center();
    let (max_x, max_y) = sector.systems.iter().fold((0., 0.), |(x_max, y_max), s| {
        (
            (sector_center.coords.x - s.location.coords.x)
                .abs()
                .max(x_max),
            (sector_center.coords.y - s.location.coords.y)
                .abs()
                .max(y_max),
        )
    });
    Canvas::default()
        .block(Block::default().title("Sector").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, system) in sector.systems.iter().enumerate() {
                let color = Color::White;
                match idx == selected {
                    true => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "X",
                        color,
                    ),
                    false => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "*",
                        color,
                    ),
                }
            }
        })
        .x_bounds([
            sector_center.coords.x - max_x,
            sector_center.coords.x + max_x,
        ])
        .y_bounds([
            sector_center.coords.y - max_y,
            sector_center.coords.y + max_y,
        ])
        .render(term, &area);
}

fn draw_system_table(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Planets", "Mass", "Orbit distance", "Temperature", "Type"].into_iter(),
        system
            .satelites
            .iter()
            .enumerate()
            .map(|(idx, ref planet)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        planet.name.clone(),
                        planet.mass.to_string(),
                        planet.orbit_distance.to_string(),
                        planet.surface_temperature.to_string(),
                        planet.planet_type.to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&system.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[15, 5, 15, 15, 5])
        .render(term, &area);
}

fn draw_system_map(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    // TODO: Find decent presentation of a system, ascii art?
}
<commit_msg>Center map around selected sector/system rather than center (#53)<commit_after>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;

use entities::Faction;
use astronomicals::sector::Sector;
use astronomicals::system::System;
use tui::widgets::{Block, Borders, Row, Table, Widget};
use tui::widgets::canvas::Canvas;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Style};

/// Level of map display.
enum Level {
    Galaxy,
    Sector,
    System,
}

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_STYLES: HashMap<Faction, Style> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Style::default().fg(Color::Red));
        m.insert(Faction::Federation, Style::default().fg(Color::Yellow));
        m.insert(Faction::Cartel, Style::default().fg(Color::Magenta));
        m.insert(Faction::Independent, Style::default().fg(Color::LightGreen));
        m
    };

    /// Styling for selected item.
    static ref SELECTED_STYLE: Style = Style::default().bg(Color::Gray);

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    level: Level,
    selected_sector: usize,
    max_selected_sector: usize,
    selected_system: usize,
    max_selected_system: usize,
    selected_astronomical: usize,
    max_selected_astronomical: usize,
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>) -> Box<Self> {
        let max_selected_sector = state.galaxy.lock().unwrap().sectors.len();

        Box::new(MapTab {
            state: state,
            level: Level::Galaxy,
            selected_sector: 0,
            max_selected_sector: max_selected_sector,
            selected_system: 0,
            max_selected_system: 0,
            selected_astronomical: 0,
            max_selected_astronomical: 0,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => match input {
                // Move up item list.
                keyevent::Key::Char('k') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector = (self.selected_sector as i32 - 1).max(0) as usize
                    }
                    Level::Sector => {
                        self.selected_system = (self.selected_system as i32 - 1).max(0) as usize
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical as i32 - 1).max(0) as usize
                    }
                },

                // Move down item list.
                keyevent::Key::Char('j') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector =
                            (self.selected_sector + 1).min(self.max_selected_sector)
                    }
                    Level::Sector => {
                        self.selected_system =
                            (self.selected_system + 1).min(self.max_selected_system)
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical + 1).min(self.max_selected_astronomical)
                    }
                },

                // Move into item.
                keyevent::Key::Char('l') => {
                    self.level = match self.level {
                        Level::Galaxy => {
                            self.max_selected_system = self.state.galaxy.lock().unwrap().sectors
                                [self.selected_sector]
                                .systems
                                .len();
                            Level::Sector
                        }
                        Level::Sector => {
                            self.max_selected_astronomical =
                                self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                                    .systems[self.selected_system]
                                    .satelites
                                    .len();
                            Level::System
                        }
                        _ => Level::Galaxy,
                    };
                }

                // Move out of item.
                keyevent::Key::Char('h') => {
                    self.level = match self.level {
                        Level::System => Level::Sector,
                        Level::Sector => Level::Galaxy,
                        _ => Level::Galaxy,
                    };
                }
                _ => {}
            },
            _ => {}
        };
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Percent(30), Size::Percent(70)])
            .render(term, area, |term, chunks| match self.level {
                Level::Galaxy => {
                    let galaxy = self.state.galaxy.lock().unwrap();
                    draw_galaxy_table(self.selected_sector, &galaxy.sectors, term, &chunks[0]);
                    draw_galaxy_map(self.selected_sector, &galaxy.sectors, term, &chunks[1]);
                }
                Level::Sector => {
                    let sector = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector];
                    draw_sector_table(self.selected_system, &sector, term, &chunks[0]);
                    draw_sector_map(self.selected_system, &sector, term, &chunks[1]);
                }
                Level::System => {
                    let system = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                        .systems[self.selected_system];
                    draw_system_table(self.selected_astronomical, &system, term, &chunks[0]);
                    draw_system_map(self.selected_astronomical, &system, term, &chunks[1]);
                }
            });
    }
}

fn draw_galaxy_table(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Sector", "Faction", "Systems"].into_iter(),
        sectors
            .iter()
            .enumerate()
            .map(|(idx, ref sector)| {
                let style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => FACTION_STYLES.get(&sector.faction).unwrap(),
                };
                Row::StyledData(
                    vec![
                        sector.name.clone(),
                        sector.faction.to_string(),
                        (sector.systems.len() as u32).to_string(),
                    ].into_iter(),
                    style,
                )
            })
            .skip(selected),
    ).block(Block::default().title("Sectors").borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_galaxy_map(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let (max_x, max_y) = sectors.iter().fold((0., 0.), |(x_max, y_max), s| {
        let location = s.center();
        (
            location.coords.x.abs().max(x_max),
            location.coords.y.abs().max(y_max),
        )
    });
    let selected_loc = sectors[selected].center();
    Canvas::default()
        .block(Block::default().title("Galaxy").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, sector) in sectors.iter().enumerate() {
                let color = match sector.faction {
                    Faction::Empire => Color::Red,
                    Faction::Federation => Color::Yellow,
                    Faction::Cartel => Color::Magenta,
                    Faction::Independent => Color::LightGreen,
                };
                let center = sector.center();
                match idx == selected {
                    true => ctx.print(center.coords.x, center.coords.y, "X", color),
                    false => ctx.print(center.coords.x, center.coords.y, "*", color),
                };
            }
        })
        .x_bounds([
            -max_x + selected_loc.coords.x,
            selected_loc.coords.x + max_x,
        ])
        .y_bounds([
            -max_y + selected_loc.coords.y,
            selected_loc.coords.y + max_y,
        ])
        .render(term, area);
}

fn draw_sector_table(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["System", "Bodies"].into_iter(),
        sector
            .systems
            .iter()
            .enumerate()
            .map(|(idx, ref system)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        system.name.clone(),
                        (system.satelites.len() as u32).to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&sector.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_sector_map(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let sector_center = sector.center();
    let (max_x, max_y) = sector.systems.iter().fold((0., 0.), |(x_max, y_max), s| {
        (
            (sector_center.coords.x - s.location.coords.x)
                .abs()
                .max(x_max),
            (sector_center.coords.y - s.location.coords.y)
                .abs()
                .max(y_max),
        )
    });
    let selected_loc = sector.systems[selected].location;
    Canvas::default()
        .block(Block::default().title("Sector").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, system) in sector.systems.iter().enumerate() {
                let color = Color::White;
                match idx == selected {
                    true => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "X",
                        color,
                    ),
                    false => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "*",
                        color,
                    ),
                }
            }
        })
        .x_bounds([selected_loc.coords.x - max_x, selected_loc.coords.x + max_x])
        .y_bounds([selected_loc.coords.y - max_y, selected_loc.coords.y + max_y])
        .render(term, &area);
}

fn draw_system_table(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Planets", "Mass", "Orbit distance", "Temperature", "Type"].into_iter(),
        system
            .satelites
            .iter()
            .enumerate()
            .map(|(idx, ref planet)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        planet.name.clone(),
                        planet.mass.to_string(),
                        planet.orbit_distance.to_string(),
                        planet.surface_temperature.to_string(),
                        planet.planet_type.to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&system.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[15, 5, 15, 15, 5])
        .render(term, &area);
}

fn draw_system_map(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    // TODO: Find decent presentation of a system, ascii art?
}
<|endoftext|>


<commit_before>use std::marker;
use std::cmp;
use std::mem;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::iter::FromIterator;

use solicit::http::client::RequestStream;
use solicit::http::client::ClientSession;
use solicit::http::session::Client;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataError;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::Stream as solicit_Stream;
use solicit::http::session::StreamIter;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::SendStatus;
use solicit::http::connection::EndStream;
use solicit::http::connection::DataChunk;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::frame::RawFrame;
use solicit::http::StreamId;
use solicit::http::HttpScheme;
use solicit::http::HttpError;
use solicit::http::HttpResult;
use solicit::http::Header;
use solicit::http::StaticHeader;

use futures;
use futures::Future;
use futures::stream::Stream;

use tokio_core;
use tokio_core::TcpStream;
use tokio_core::io as tokio_io;
use tokio_core::io::TaskIo;
use tokio_core::io::TaskIoRead;
use tokio_core::io::TaskIoWrite;
use tokio_core::LoopHandle;

use error::*;
use futures_misc::*;

use solicit_async::*;
use solicit_misc::*;

// TODO: make async
pub trait HttpResponseHandler: Send + 'static {
    // called once response headers received
    fn headers(&mut self, headers: Vec<StaticHeader>) -> bool;
    // called for each response data frame
    fn data_frame(&mut self, chunk: Vec<u8>) -> bool;
    // called for response trailers
    fn trailers(&mut self, headers: Vec<StaticHeader>) -> bool;
    // called at the end of reponse stream
    fn end(&mut self);
}

enum ResponseState {
    Init,
    GotHeaders,
    GotBodyChunk,
    GotTrailers,
}

enum LastChunk {
    Empty,
    Headers(Vec<StaticHeader>),
    Chunk(Vec<u8>),
    Trailers(Vec<StaticHeader>),
}

struct GrpcHttp2ClientStream2<H : HttpResponseHandler> {
    state: StreamState,
    response_state: ResponseState,
    last_chunk: LastChunk,
    response_handler: Option<H>,
}

impl<H : HttpResponseHandler> solicit_Stream for GrpcHttp2ClientStream2<H> {
    fn set_headers<'n, 'v>(&mut self, headers: Vec<Header<'n, 'v>>) {
        let headers = headers.into_iter().map(|h| Header::new(h.name().to_owned(), h.value().to_owned())).collect();
        let (last_chunk, response_state) = match self.response_state {
            ResponseState::Init => (LastChunk::Headers(headers), ResponseState::GotHeaders),
            ResponseState::GotHeaders => panic!(),
            ResponseState::GotBodyChunk => (LastChunk::Trailers(headers), ResponseState::GotTrailers),
            ResponseState::GotTrailers => panic!(),
        };
        self.last_chunk = last_chunk;
        self.response_state = response_state;
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        self.last_chunk = LastChunk::Chunk(data.to_owned());
        self.response_state = ResponseState::GotBodyChunk;
    }

    fn set_state(&mut self, state: StreamState) {
        self.state = state;
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        unimplemented!()
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

struct MySessionState<H : HttpResponseHandler> {
    streams: HashMap<StreamId, GrpcHttp2ClientStream2<H>>,
    next_stream_id: StreamId,
}

impl<H : HttpResponseHandler> MySessionState<H> {
    fn process_streams_after_handle_next_frame(&mut self) {
        let mut remove_ids = Vec::new();

        for (id, s) in &mut self.streams {
            let mut ok = false;
            if let Some(ref mut response_handler) = s.response_handler {
                let last_chunk = mem::replace(&mut s.last_chunk, LastChunk::Empty);
                ok = match last_chunk {
                    LastChunk::Empty => true,
                    LastChunk::Chunk(chunk) => {
                        response_handler.data_frame(chunk)
                    }
                    LastChunk::Headers(headers) => {
                        response_handler.headers(headers)
                    }
                    LastChunk::Trailers(headers) => {
                        let r = response_handler.trailers(headers);
                        if r {
                            response_handler.end();
                        }
                        r
                    }
                };

            }

            if !ok {
                s.response_handler.take();
            }

            if s.is_closed() {
                remove_ids.push(*id);
            }
        }

        Vec::from_iter(remove_ids.into_iter().map(|i| self.remove_stream(i).unwrap()));
    }
}

impl<H : HttpResponseHandler> SessionState for MySessionState<H> {
    type Stream = GrpcHttp2ClientStream2<H>;

    fn insert_outgoing(&mut self, stream: Self::Stream) -> StreamId {
        let id = self.next_stream_id;
        self.streams.insert(id, stream);
        self.next_stream_id += 2;
        id
    }

    fn insert_incoming(&mut self, stream_id: StreamId, stream: Self::Stream) -> Result<(), ()> {
        // TODO: assert parity
        // TODO: Assert that the stream IDs are monotonically increasing!
        self.streams.insert(stream_id, stream);
        Ok(())
    }

    #[inline]
    fn get_stream_ref(&self, stream_id: StreamId) -> Option<&Self::Stream> {
        self.streams.get(&stream_id)
    }

    #[inline]
    fn get_stream_mut(&mut self, stream_id: StreamId) -> Option<&mut Self::Stream> {
        self.streams.get_mut(&stream_id)
    }

    #[inline]
    fn remove_stream(&mut self, stream_id: StreamId) -> Option<Self::Stream> {
        self.streams.remove(&stream_id)
    }

    #[inline]
    fn iter(&mut self) -> StreamIter<GrpcHttp2ClientStream2<H>> {
        // https://github.com/mlalic/solicit/pull/34
        unimplemented!()
    }

    /// Number of currently active streams
    #[inline]
    fn len(&self) -> usize {
        self.streams.len()
    }
}

struct Inner<H : HttpResponseHandler> {
    conn: HttpConnection,
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
    session_state: MySessionState<H>,
}

pub struct HttpConnectionAsync<H : HttpResponseHandler> {
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
}

struct StartRequestMessage<H : HttpResponseHandler> {
    headers: Vec<StaticHeader>,
    body: HttpStream<Vec<u8>>,
    response_handler: H,
}

struct BodyChunkMessage {
    stream_id: StreamId,
    chunk: Vec<u8>,
}

struct EndRequestMessage {
    stream_id: StreamId,
}

struct ReadToWriteMessage {
    buf: Vec<u8>,
}

enum ToWriteMessage<H : HttpResponseHandler> {
    Start(StartRequestMessage<H>),
    BodyChunk(BodyChunkMessage),
    End(EndRequestMessage),
    FromRead(ReadToWriteMessage),
}

struct WriteLoop<H : HttpResponseHandler> {
    write: TaskIoWrite<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : HttpResponseHandler> WriteLoop<H> {
    fn write_all(self, buf: Vec<u8>) -> HttpFuture<Self> {
        let WriteLoop { write, inner } = self;

        tokio_io::write_all(write, buf)
            .map(move |(write, _)| WriteLoop { write: write, inner: inner })
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_from_read(self, message: ReadToWriteMessage) -> HttpFuture<Self> {
        self.write_all(message.buf)
    }

    fn process_start(self, start: StartRequestMessage<H>) -> HttpFuture<Self> {
        let StartRequestMessage { headers, body, response_handler } = start;
        let (buf, stream_id) = self.inner.with(move |inner: &mut Inner<H>| {

            let stream = GrpcHttp2ClientStream2 {
                state: StreamState::Open,
                response_handler: Some(response_handler),
                last_chunk: LastChunk::Empty,
                response_state: ResponseState::Init,
            };
            let stream_id = inner.session_state.insert_outgoing(stream);

            let send_buf = {
                let mut send_buf = VecSendFrame(Vec::new());

                inner.conn.sender(&mut send_buf).send_headers(headers, stream_id, EndStream::No).unwrap();

                (send_buf.0, stream_id)
            };

            send_buf
        });

        self.write_all(buf)
            .and_then(move |wl: WriteLoop<H>| {
                body.fold(wl, move |wl: WriteLoop<H>, chunk| {
                    wl.inner.with(|inner: &mut Inner<H>| {
                        inner.call_tx.send(ToWriteMessage::BodyChunk(BodyChunkMessage {
                            stream_id: stream_id,
                            chunk: chunk,
                        }))
                    });
                    futures::finished::<_, HttpError>(wl)
                })
            })
            .and_then(move |wl: WriteLoop<H>| {
                wl.inner.with(|inner: &mut Inner<H>| {
                    inner.call_tx.send(ToWriteMessage::End(EndRequestMessage {
                        stream_id: stream_id,
                    }));
                });
                futures::finished::<_, HttpError>(wl)
            })
            .boxed()
    }

    fn process_body_chunk(self, body_chunk: BodyChunkMessage) -> HttpFuture<Self> {
        println!("client: process request body chunk");

        let BodyChunkMessage { stream_id, chunk } = body_chunk;

        let buf = self.inner.with(move |inner: &mut Inner<H>| {
            {
                let stream = inner.session_state.get_stream_mut(stream_id);
                // TODO: check stream state
            }

            let mut send_buf = VecSendFrame(Vec::new());

            let mut pos = 0;
            const MAX_CHUNK_SIZE: usize = 8 * 1024;
            while pos < chunk.len() {
                let end = cmp::min(chunk.len(), pos + MAX_CHUNK_SIZE);

                let data_chunk = DataChunk::new_borrowed(&chunk[pos..end], stream_id, EndStream::No);

                inner.conn.sender(&mut send_buf).send_data(data_chunk).unwrap();

                pos = end;
            }

            send_buf.0
        });

        self.write_all(buf)
    }

    fn process_end(self, end: EndRequestMessage) -> HttpFuture<Self> {
        println!("client: process request end");
        let EndRequestMessage { stream_id } = end;

        let buf = self.inner.with(move |inner: &mut Inner<H>| {
            let mut send_buf = VecSendFrame(Vec::new());

            let chunk = Vec::new();
            let data_chunk = DataChunk::new_borrowed(&chunk[..], stream_id, EndStream::Yes);

            inner.conn.sender(&mut send_buf).send_data(data_chunk).unwrap();

            send_buf.0
        });

        self.write_all(buf)
    }

    fn process_message(self, message: ToWriteMessage<H>) -> HttpFuture<Self> {
        match message {
            ToWriteMessage::Start(start) => self.process_start(start),
            ToWriteMessage::BodyChunk(body_chunk) => self.process_body_chunk(body_chunk),
            ToWriteMessage::End(end) => self.process_end(end),
            ToWriteMessage::FromRead(from_read) => self.process_from_read(from_read),
        }
    }

    fn run(self, requests: HttpStream<ToWriteMessage<H>>) -> HttpFuture<()> {
        let requests = requests.map_err(HttpError::from);
        requests
            .fold(self, move |wl, message: ToWriteMessage<H>| {
                wl.process_message(message)
            })
            .map(|_| ())
            .boxed()
    }
}

struct ReadLoop<H : HttpResponseHandler> {
    read: TaskIoRead<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : HttpResponseHandler> ReadLoop<H> {
    fn recv_raw_frame(self) -> HttpFuture<(Self, RawFrame<'static>)> {
        let ReadLoop { read, inner } = self;
        recv_raw_frame(read)
            .map(|(read, frame)| (ReadLoop { read: read, inner: inner}, frame))
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_streams_after_handle_next_frame(self) -> HttpFuture<Self> {
        self.inner.with(|inner: &mut Inner<H>| {
            inner.session_state.process_streams_after_handle_next_frame();
        });

        futures::finished(self).boxed()
    }

    fn process_raw_frame(self, raw_frame: RawFrame) -> HttpFuture<Self> {
        self.inner.with(move |inner: &mut Inner<H>| {
            let mut send = VecSendFrame(Vec::new());
            {
                let mut session = ClientSession::new(&mut inner.session_state, &mut send);
                inner.conn.handle_next_frame(&mut OnceReceiveFrame::new(raw_frame), &mut session)
                    .unwrap();
            }

            if !send.0.is_empty() {
                inner.call_tx.send(ToWriteMessage::FromRead(ReadToWriteMessage { buf: send.0 }))
                    .expect("read to write");
            }
        });

        self.process_streams_after_handle_next_frame()
    }

    fn read_process_frame(self) -> HttpFuture<Self> {
        self.recv_raw_frame()
            .and_then(move |(rl, frame)| rl.process_raw_frame(frame))
            .boxed()
    }

    fn run(self) -> HttpFuture<()> {
        let stream = stream_repeat(());

        let future = stream.fold(self, |rl, _| {
            rl.read_process_frame()
        });

        future.map(|_| ()).boxed()
    }
}

impl<H : HttpResponseHandler> HttpConnectionAsync<H> {
    pub fn new(lh: LoopHandle, addr: &SocketAddr) -> (Self, HttpFuture<()>) {
        let (call_tx, call_rx) = lh.clone().channel();

        let call_rx = future_flatten_to_stream(call_rx).map_err(HttpError::from).boxed();

        let c = HttpConnectionAsync {
            call_tx: call_tx.clone(),
        };

        let handshake = connect_and_handshake(lh, addr);
        let future = handshake.and_then(move |conn| {
            let (read, write) = TaskIo::new(conn).split();

            let inner = TaskRcMut::new(Inner {
                conn: HttpConnection::new(HttpScheme::Http),
                call_tx: call_tx.clone(),
                session_state: MySessionState {
                    next_stream_id: 1,
                    streams: HashMap::new(),
                }
            });

            let write_inner = inner.clone();
            let run_write = WriteLoop { write: write, inner: write_inner }.run(call_rx);
            let run_read = ReadLoop { read: read, inner: inner.clone() }.run();

            run_write.join(run_read).map(|_| ())
        }).boxed();

        (c, future)
    }

    pub fn start_request(
        &self,
        headers: Vec<StaticHeader>,
        body: HttpStream<Vec<u8>>,
        response_handler: H)
            -> HttpFuture<()>
    {
        self.call_tx.send(ToWriteMessage::Start(StartRequestMessage {
            headers: headers,
            body: body,
            response_handler: response_handler,
        }));

        // should use bounded queue here, so future result
        futures::finished(()).boxed()
    }
}
<commit_msg>cleanup<commit_after>use std::marker;
use std::cmp;
use std::mem;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::iter::FromIterator;

use solicit::http::client::RequestStream;
use solicit::http::client::ClientSession;
use solicit::http::session::Client;
use solicit::http::session::SessionState;
use solicit::http::session::DefaultSessionState;
use solicit::http::session::DefaultStream;
use solicit::http::session::StreamState;
use solicit::http::session::StreamDataError;
use solicit::http::session::StreamDataChunk;
use solicit::http::session::Stream as solicit_Stream;
use solicit::http::session::StreamIter;
use solicit::http::connection::HttpConnection;
use solicit::http::connection::SendStatus;
use solicit::http::connection::EndStream;
use solicit::http::connection::DataChunk;
use solicit::http::priority::SimplePrioritizer;
use solicit::http::frame::RawFrame;
use solicit::http::StreamId;
use solicit::http::HttpScheme;
use solicit::http::HttpError;
use solicit::http::HttpResult;
use solicit::http::Header;
use solicit::http::StaticHeader;

use futures;
use futures::Future;
use futures::stream::Stream;

use tokio_core;
use tokio_core::TcpStream;
use tokio_core::io as tokio_io;
use tokio_core::io::TaskIo;
use tokio_core::io::TaskIoRead;
use tokio_core::io::TaskIoWrite;
use tokio_core::LoopHandle;

use error::*;
use futures_misc::*;

use solicit_async::*;
use solicit_misc::*;

// TODO: make async
pub trait HttpResponseHandler: Send + 'static {
    // called once response headers received
    fn headers(&mut self, headers: Vec<StaticHeader>) -> bool;
    // called for each response data frame
    fn data_frame(&mut self, chunk: Vec<u8>) -> bool;
    // called for response trailers
    fn trailers(&mut self, headers: Vec<StaticHeader>) -> bool;
    // called at the end of reponse stream
    fn end(&mut self);
}

enum ResponseState {
    Init,
    GotHeaders,
    GotBodyChunk,
    GotTrailers,
}

enum LastChunk {
    Empty,
    Headers(Vec<StaticHeader>),
    Chunk(Vec<u8>),
    Trailers(Vec<StaticHeader>),
}

struct GrpcHttp2ClientStream2<H : HttpResponseHandler> {
    state: StreamState,
    response_state: ResponseState,
    last_chunk: LastChunk,
    response_handler: Option<H>,
}

impl<H : HttpResponseHandler> solicit_Stream for GrpcHttp2ClientStream2<H> {
    fn set_headers<'n, 'v>(&mut self, headers: Vec<Header<'n, 'v>>) {
        let headers = headers.into_iter().map(|h| Header::new(h.name().to_owned(), h.value().to_owned())).collect();
        let (last_chunk, response_state) = match self.response_state {
            ResponseState::Init => (LastChunk::Headers(headers), ResponseState::GotHeaders),
            ResponseState::GotHeaders => panic!(),
            ResponseState::GotBodyChunk => (LastChunk::Trailers(headers), ResponseState::GotTrailers),
            ResponseState::GotTrailers => panic!(),
        };
        self.last_chunk = last_chunk;
        self.response_state = response_state;
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        self.last_chunk = LastChunk::Chunk(data.to_owned());
        self.response_state = ResponseState::GotBodyChunk;
    }

    fn set_state(&mut self, state: StreamState) {
        self.state = state;
    }

    fn get_data_chunk(&mut self, buf: &mut [u8]) -> Result<StreamDataChunk, StreamDataError> {
        unimplemented!()
    }

    fn state(&self) -> StreamState {
        self.state
    }
}

struct MySessionState<H : HttpResponseHandler> {
    streams: HashMap<StreamId, GrpcHttp2ClientStream2<H>>,
    next_stream_id: StreamId,
}

impl<H : HttpResponseHandler> MySessionState<H> {
    fn process_streams_after_handle_next_frame(&mut self) {
        let mut remove_ids = Vec::new();

        for (id, s) in &mut self.streams {
            let mut ok = false;
            if let Some(ref mut response_handler) = s.response_handler {
                let last_chunk = mem::replace(&mut s.last_chunk, LastChunk::Empty);
                ok = match last_chunk {
                    LastChunk::Empty => true,
                    LastChunk::Chunk(chunk) => {
                        response_handler.data_frame(chunk)
                    }
                    LastChunk::Headers(headers) => {
                        response_handler.headers(headers)
                    }
                    LastChunk::Trailers(headers) => {
                        let r = response_handler.trailers(headers);
                        if r {
                            response_handler.end();
                        }
                        r
                    }
                };

            }

            if !ok {
                s.response_handler.take();
            }

            if s.is_closed() {
                remove_ids.push(*id);
            }
        }

        Vec::from_iter(remove_ids.into_iter().map(|i| self.remove_stream(i).unwrap()));
    }
}

impl<H : HttpResponseHandler> SessionState for MySessionState<H> {
    type Stream = GrpcHttp2ClientStream2<H>;

    fn insert_outgoing(&mut self, stream: Self::Stream) -> StreamId {
        let id = self.next_stream_id;
        self.streams.insert(id, stream);
        self.next_stream_id += 2;
        id
    }

    fn insert_incoming(&mut self, stream_id: StreamId, stream: Self::Stream) -> Result<(), ()> {
        panic!("unused on the client");
    }

    fn get_stream_ref(&self, stream_id: StreamId) -> Option<&Self::Stream> {
        self.streams.get(&stream_id)
    }

    fn get_stream_mut(&mut self, stream_id: StreamId) -> Option<&mut Self::Stream> {
        self.streams.get_mut(&stream_id)
    }

    fn remove_stream(&mut self, stream_id: StreamId) -> Option<Self::Stream> {
        self.streams.remove(&stream_id)
    }

    fn iter(&mut self) -> StreamIter<GrpcHttp2ClientStream2<H>> {
        // https://github.com/mlalic/solicit/pull/34
        unimplemented!()
    }

    /// Number of currently active streams
    fn len(&self) -> usize {
        self.streams.len()
    }
}

struct Inner<H : HttpResponseHandler> {
    conn: HttpConnection,
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
    session_state: MySessionState<H>,
}

pub struct HttpConnectionAsync<H : HttpResponseHandler> {
    call_tx: tokio_core::Sender<ToWriteMessage<H>>,
}

struct StartRequestMessage<H : HttpResponseHandler> {
    headers: Vec<StaticHeader>,
    body: HttpStream<Vec<u8>>,
    response_handler: H,
}

struct BodyChunkMessage {
    stream_id: StreamId,
    chunk: Vec<u8>,
}

struct EndRequestMessage {
    stream_id: StreamId,
}

struct ReadToWriteMessage {
    buf: Vec<u8>,
}

enum ToWriteMessage<H : HttpResponseHandler> {
    Start(StartRequestMessage<H>),
    BodyChunk(BodyChunkMessage),
    End(EndRequestMessage),
    FromRead(ReadToWriteMessage),
}

struct WriteLoop<H : HttpResponseHandler> {
    write: TaskIoWrite<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : HttpResponseHandler> WriteLoop<H> {
    fn write_all(self, buf: Vec<u8>) -> HttpFuture<Self> {
        let WriteLoop { write, inner } = self;

        tokio_io::write_all(write, buf)
            .map(move |(write, _)| WriteLoop { write: write, inner: inner })
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_from_read(self, message: ReadToWriteMessage) -> HttpFuture<Self> {
        self.write_all(message.buf)
    }

    fn process_start(self, start: StartRequestMessage<H>) -> HttpFuture<Self> {
        let StartRequestMessage { headers, body, response_handler } = start;
        let (buf, stream_id) = self.inner.with(move |inner: &mut Inner<H>| {

            let stream = GrpcHttp2ClientStream2 {
                state: StreamState::Open,
                response_handler: Some(response_handler),
                last_chunk: LastChunk::Empty,
                response_state: ResponseState::Init,
            };
            let stream_id = inner.session_state.insert_outgoing(stream);

            let send_buf = {
                let mut send_buf = VecSendFrame(Vec::new());

                inner.conn.sender(&mut send_buf).send_headers(headers, stream_id, EndStream::No).unwrap();

                (send_buf.0, stream_id)
            };

            send_buf
        });

        self.write_all(buf)
            .and_then(move |wl: WriteLoop<H>| {
                body.fold(wl, move |wl: WriteLoop<H>, chunk| {
                    wl.inner.with(|inner: &mut Inner<H>| {
                        inner.call_tx.send(ToWriteMessage::BodyChunk(BodyChunkMessage {
                            stream_id: stream_id,
                            chunk: chunk,
                        }))
                    });
                    futures::finished::<_, HttpError>(wl)
                })
            })
            .and_then(move |wl: WriteLoop<H>| {
                wl.inner.with(|inner: &mut Inner<H>| {
                    inner.call_tx.send(ToWriteMessage::End(EndRequestMessage {
                        stream_id: stream_id,
                    }));
                });
                futures::finished::<_, HttpError>(wl)
            })
            .boxed()
    }

    fn process_body_chunk(self, body_chunk: BodyChunkMessage) -> HttpFuture<Self> {
        println!("client: process request body chunk");

        let BodyChunkMessage { stream_id, chunk } = body_chunk;

        let buf = self.inner.with(move |inner: &mut Inner<H>| {
            {
                let stream = inner.session_state.get_stream_mut(stream_id);
                // TODO: check stream state
            }

            let mut send_buf = VecSendFrame(Vec::new());

            let mut pos = 0;
            const MAX_CHUNK_SIZE: usize = 8 * 1024;
            while pos < chunk.len() {
                let end = cmp::min(chunk.len(), pos + MAX_CHUNK_SIZE);

                let data_chunk = DataChunk::new_borrowed(&chunk[pos..end], stream_id, EndStream::No);

                inner.conn.sender(&mut send_buf).send_data(data_chunk).unwrap();

                pos = end;
            }

            send_buf.0
        });

        self.write_all(buf)
    }

    fn process_end(self, end: EndRequestMessage) -> HttpFuture<Self> {
        println!("client: process request end");
        let EndRequestMessage { stream_id } = end;

        let buf = self.inner.with(move |inner: &mut Inner<H>| {
            let mut send_buf = VecSendFrame(Vec::new());

            let chunk = Vec::new();
            let data_chunk = DataChunk::new_borrowed(&chunk[..], stream_id, EndStream::Yes);

            inner.conn.sender(&mut send_buf).send_data(data_chunk).unwrap();

            send_buf.0
        });

        self.write_all(buf)
    }

    fn process_message(self, message: ToWriteMessage<H>) -> HttpFuture<Self> {
        match message {
            ToWriteMessage::Start(start) => self.process_start(start),
            ToWriteMessage::BodyChunk(body_chunk) => self.process_body_chunk(body_chunk),
            ToWriteMessage::End(end) => self.process_end(end),
            ToWriteMessage::FromRead(from_read) => self.process_from_read(from_read),
        }
    }

    fn run(self, requests: HttpStream<ToWriteMessage<H>>) -> HttpFuture<()> {
        let requests = requests.map_err(HttpError::from);
        requests
            .fold(self, move |wl, message: ToWriteMessage<H>| {
                wl.process_message(message)
            })
            .map(|_| ())
            .boxed()
    }
}

struct ReadLoop<H : HttpResponseHandler> {
    read: TaskIoRead<TcpStream>,
    inner: TaskRcMut<Inner<H>>,
}

impl<H : HttpResponseHandler> ReadLoop<H> {
    fn recv_raw_frame(self) -> HttpFuture<(Self, RawFrame<'static>)> {
        let ReadLoop { read, inner } = self;
        recv_raw_frame(read)
            .map(|(read, frame)| (ReadLoop { read: read, inner: inner}, frame))
            .map_err(HttpError::from)
            .boxed()
    }

    fn process_streams_after_handle_next_frame(self) -> HttpFuture<Self> {
        self.inner.with(|inner: &mut Inner<H>| {
            inner.session_state.process_streams_after_handle_next_frame();
        });

        futures::finished(self).boxed()
    }

    fn process_raw_frame(self, raw_frame: RawFrame) -> HttpFuture<Self> {
        self.inner.with(move |inner: &mut Inner<H>| {
            let mut send = VecSendFrame(Vec::new());
            {
                let mut session = ClientSession::new(&mut inner.session_state, &mut send);
                inner.conn.handle_next_frame(&mut OnceReceiveFrame::new(raw_frame), &mut session)
                    .unwrap();
            }

            if !send.0.is_empty() {
                inner.call_tx.send(ToWriteMessage::FromRead(ReadToWriteMessage { buf: send.0 }))
                    .expect("read to write");
            }
        });

        self.process_streams_after_handle_next_frame()
    }

    fn read_process_frame(self) -> HttpFuture<Self> {
        self.recv_raw_frame()
            .and_then(move |(rl, frame)| rl.process_raw_frame(frame))
            .boxed()
    }

    fn run(self) -> HttpFuture<()> {
        let stream = stream_repeat(());

        let future = stream.fold(self, |rl, _| {
            rl.read_process_frame()
        });

        future.map(|_| ()).boxed()
    }
}

impl<H : HttpResponseHandler> HttpConnectionAsync<H> {
    pub fn new(lh: LoopHandle, addr: &SocketAddr) -> (Self, HttpFuture<()>) {
        let (call_tx, call_rx) = lh.clone().channel();

        let call_rx = future_flatten_to_stream(call_rx).map_err(HttpError::from).boxed();

        let c = HttpConnectionAsync {
            call_tx: call_tx.clone(),
        };

        let handshake = connect_and_handshake(lh, addr);
        let future = handshake.and_then(move |conn| {
            let (read, write) = TaskIo::new(conn).split();

            let inner = TaskRcMut::new(Inner {
                conn: HttpConnection::new(HttpScheme::Http),
                call_tx: call_tx.clone(),
                session_state: MySessionState {
                    next_stream_id: 1,
                    streams: HashMap::new(),
                }
            });

            let write_inner = inner.clone();
            let run_write = WriteLoop { write: write, inner: write_inner }.run(call_rx);
            let run_read = ReadLoop { read: read, inner: inner.clone() }.run();

            run_write.join(run_read).map(|_| ())
        }).boxed();

        (c, future)
    }

    pub fn start_request(
        &self,
        headers: Vec<StaticHeader>,
        body: HttpStream<Vec<u8>>,
        response_handler: H)
            -> HttpFuture<()>
    {
        self.call_tx.send(ToWriteMessage::Start(StartRequestMessage {
            headers: headers,
            body: body,
            response_handler: response_handler,
        }));

        // should use bounded queue here, so future result
        futures::finished(()).boxed()
    }
}
<|endoftext|>