<commit_before>use std::string::*;
use std::fmt;

#[derive(Debug,Copy,Clone,PartialEq)]
pub struct Soniton {
    pub bits: u8,
    pub be: bool,
    pub packed: bool,
    pub planar: bool,
    pub float: bool,
    pub signed: bool,
}

// TODO: make it a trait for usize?
fn align(v: usize, a: usize) -> usize {
    (v + a - 1) & !(a - 1)
}

fn round_to_byte(v: usize) -> usize {
    (v + 7) >> 3
}

impl Soniton {
    pub fn new(bits: u8, be: bool, packed: bool, planar: bool, float: bool, signed: bool) -> Self {
        Soniton {
            bits: bits,
            be: be,
            packed: packed,
            planar: planar,
            float: float,
            signed: signed,
        }
    }

    pub fn get_audio_size(&self, length: usize, alignment: usize) -> usize {
        let s = if self.packed {
            round_to_byte(length * (self.bits as usize))
        } else {
            length * round_to_byte(self.bits as usize)
        };

        align(s, alignment)
    }
}

impl fmt::Display for Soniton {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let fmt = if self.float {
            "float"
        } else if self.signed {
            "int"
        } else {
            "uint"
        };
        let end = if self.be {
            "BE"
        } else {
            "LE"
        };
        write!(f,
               "({} bps, {} planar: {} packed: {} {})",
               self.bits,
               end,
               self.packed,
               self.planar,
               fmt)
    }
}

#[derive(Debug,Clone,Copy,PartialEq)]
pub enum ChannelType {
    C,
    L,
    R,
    Cs,
    Ls,
    Rs,
    Lss,
    Rss,
    LFE,
    Lc,
    Rc,
    Lh,
    Rh,
    Ch,
    LFE2,
    Lw,
    Rw,
    Ov,
    Lhs,
    Rhs,
    Chs,
    Ll,
    Rl,
    Cl,
    Lt,
    Rt,
    Lo,
    Ro,
}

impl ChannelType {
    pub fn is_center(&self) -> bool {
        match *self {
            ChannelType::C => true,
            ChannelType::Ch => true,
            ChannelType::Cl => true,
            ChannelType::Ov => true,
            ChannelType::LFE => true,
            ChannelType::LFE2 => true,
            ChannelType::Cs => true,
            ChannelType::Chs => true,
            _ => false,
        }
    }
    pub fn is_left(&self) -> bool {
        match *self {
            ChannelType::L => true,
            ChannelType::Ls => true,
            ChannelType::Lss => true,
            ChannelType::Lc => true,
            ChannelType::Lh => true,
            ChannelType::Lw => true,
            ChannelType::Lhs => true,
            ChannelType::Ll => true,
            ChannelType::Lt => true,
            ChannelType::Lo => true,
            _ => false,
        }
    }
    pub fn is_right(&self) -> bool {
        match *self {
            ChannelType::R => true,
            ChannelType::Rs => true,
            ChannelType::Rss => true,
            ChannelType::Rc => true,
            ChannelType::Rh => true,
            ChannelType::Rw => true,
            ChannelType::Rhs => true,
            ChannelType::Rl => true,
            ChannelType::Rt => true,
            ChannelType::Ro => true,
            _ => false,
        }
    }
}

impl fmt::Display for ChannelType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            ChannelType::C => "C".to_string(),
            ChannelType::L => "L".to_string(),
            ChannelType::R => "R".to_string(),
            ChannelType::Cs => "Cs".to_string(),
            ChannelType::Ls => "Ls".to_string(),
            ChannelType::Rs => "Rs".to_string(),
            ChannelType::Lss => "Lss".to_string(),
            ChannelType::Rss => "Rss".to_string(),
            ChannelType::LFE => "LFE".to_string(),
            ChannelType::Lc => "Lc".to_string(),
            ChannelType::Rc => "Rc".to_string(),
            ChannelType::Lh => "Lh".to_string(),
            ChannelType::Rh => "Rh".to_string(),
            ChannelType::Ch => "Ch".to_string(),
            ChannelType::LFE2 => "LFE2".to_string(),
            ChannelType::Lw => "Lw".to_string(),
            ChannelType::Rw => "Rw".to_string(),
            ChannelType::Ov => "Ov".to_string(),
            ChannelType::Lhs => "Lhs".to_string(),
            ChannelType::Rhs => "Rhs".to_string(),
            ChannelType::Chs => "Chs".to_string(),
            ChannelType::Ll => "Ll".to_string(),
            ChannelType::Rl => "Rl".to_string(),
            ChannelType::Cl => "Cl".to_string(),
            ChannelType::Lt => "Lt".to_string(),
            ChannelType::Rt => "Rt".to_string(),
            ChannelType::Lo => "Lo".to_string(),
            ChannelType::Ro => "Ro".to_string(),
        };
        write!(f, "{}", name)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub struct ChannelMap {
    ids: Vec<ChannelType>,
}

impl ChannelMap {
    pub fn new() -> Self {
        ChannelMap { ids: Vec::new() }
    }
    pub fn add_channel(&mut self, ch: ChannelType) {
        self.ids.push(ch);
    }
    pub fn add_channels(&mut self, chs: &[ChannelType]) {
        for i in 0..chs.len() {
            self.ids.push(chs[i]);
        }
    }
    pub fn len(&self) -> usize {
        self.ids.len()
    }
    pub fn get_channel(&self, idx: usize) -> ChannelType {
        self.ids[idx]
    }
    pub fn find_channel_id(&self, t: ChannelType) -> Option<u8> {
        for i in 0..self.ids.len() {
            if self.ids[i] as i32 == t as i32 {
                return Some(i as u8);
            }
        }
        None
    }
    pub fn default_map(count: usize) -> Self {
        use self::ChannelType::*;
        let ids = match count {
            1 => vec![C],
            2 => vec![R, L],
            _ => unimplemented!()
        };

        ChannelMap { ids }
    }
}

pub mod formats {
    use super::*;

    pub const U8: Soniton = Soniton {
        bits: 8,
        be: false,
        packed: false,
        planar: false,
        float: false,
        signed: false,
    };
    pub const S16: Soniton = Soniton {
        bits: 16,
        be: false,
        packed: false,
        planar: false,
        float: false,
        signed: true,
    };
    pub const F32: Soniton = Soniton {
        bits: 32,
        be: false,
        packed: false,
        planar: false,
        float: true,
        signed: true,
    };
}

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

    #[test]
    fn fmt() {
        println!("{}", formats::S16);
        println!("{}", formats::U8);
        println!("{}", formats::F32);
    }
}
<commit_msg>data: Add F64 and S32 Sonitons<commit_after>use std::string::*;
use std::fmt;

#[derive(Debug,Copy,Clone,PartialEq)]
pub struct Soniton {
    pub bits: u8,
    pub be: bool,
    pub packed: bool,
    pub planar: bool,
    pub float: bool,
    pub signed: bool,
}

// TODO: make it a trait for usize?
fn align(v: usize, a: usize) -> usize {
    (v + a - 1) & !(a - 1)
}

fn round_to_byte(v: usize) -> usize {
    (v + 7) >> 3
}

impl Soniton {
    pub fn new(bits: u8, be: bool, packed: bool, planar: bool, float: bool, signed: bool) -> Self {
        Soniton {
            bits: bits,
            be: be,
            packed: packed,
            planar: planar,
            float: float,
            signed: signed,
        }
    }

    pub fn get_audio_size(&self, length: usize, alignment: usize) -> usize {
        let s = if self.packed {
            round_to_byte(length * (self.bits as usize))
        } else {
            length * round_to_byte(self.bits as usize)
        };

        align(s, alignment)
    }
}

impl fmt::Display for Soniton {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let fmt = if self.float {
            "float"
        } else if self.signed {
            "int"
        } else {
            "uint"
        };
        let end = if self.be {
            "BE"
        } else {
            "LE"
        };
        write!(f,
               "({} bps, {} planar: {} packed: {} {})",
               self.bits,
               end,
               self.packed,
               self.planar,
               fmt)
    }
}

#[derive(Debug,Clone,Copy,PartialEq)]
pub enum ChannelType {
    C,
    L,
    R,
    Cs,
    Ls,
    Rs,
    Lss,
    Rss,
    LFE,
    Lc,
    Rc,
    Lh,
    Rh,
    Ch,
    LFE2,
    Lw,
    Rw,
    Ov,
    Lhs,
    Rhs,
    Chs,
    Ll,
    Rl,
    Cl,
    Lt,
    Rt,
    Lo,
    Ro,
}

impl ChannelType {
    pub fn is_center(&self) -> bool {
        match *self {
            ChannelType::C => true,
            ChannelType::Ch => true,
            ChannelType::Cl => true,
            ChannelType::Ov => true,
            ChannelType::LFE => true,
            ChannelType::LFE2 => true,
            ChannelType::Cs => true,
            ChannelType::Chs => true,
            _ => false,
        }
    }
    pub fn is_left(&self) -> bool {
        match *self {
            ChannelType::L => true,
            ChannelType::Ls => true,
            ChannelType::Lss => true,
            ChannelType::Lc => true,
            ChannelType::Lh => true,
            ChannelType::Lw => true,
            ChannelType::Lhs => true,
            ChannelType::Ll => true,
            ChannelType::Lt => true,
            ChannelType::Lo => true,
            _ => false,
        }
    }
    pub fn is_right(&self) -> bool {
        match *self {
            ChannelType::R => true,
            ChannelType::Rs => true,
            ChannelType::Rss => true,
            ChannelType::Rc => true,
            ChannelType::Rh => true,
            ChannelType::Rw => true,
            ChannelType::Rhs => true,
            ChannelType::Rl => true,
            ChannelType::Rt => true,
            ChannelType::Ro => true,
            _ => false,
        }
    }
}

impl fmt::Display for ChannelType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            ChannelType::C => "C".to_string(),
            ChannelType::L => "L".to_string(),
            ChannelType::R => "R".to_string(),
            ChannelType::Cs => "Cs".to_string(),
            ChannelType::Ls => "Ls".to_string(),
            ChannelType::Rs => "Rs".to_string(),
            ChannelType::Lss => "Lss".to_string(),
            ChannelType::Rss => "Rss".to_string(),
            ChannelType::LFE => "LFE".to_string(),
            ChannelType::Lc => "Lc".to_string(),
            ChannelType::Rc => "Rc".to_string(),
            ChannelType::Lh => "Lh".to_string(),
            ChannelType::Rh => "Rh".to_string(),
            ChannelType::Ch => "Ch".to_string(),
            ChannelType::LFE2 => "LFE2".to_string(),
            ChannelType::Lw => "Lw".to_string(),
            ChannelType::Rw => "Rw".to_string(),
            ChannelType::Ov => "Ov".to_string(),
            ChannelType::Lhs => "Lhs".to_string(),
            ChannelType::Rhs => "Rhs".to_string(),
            ChannelType::Chs => "Chs".to_string(),
            ChannelType::Ll => "Ll".to_string(),
            ChannelType::Rl => "Rl".to_string(),
            ChannelType::Cl => "Cl".to_string(),
            ChannelType::Lt => "Lt".to_string(),
            ChannelType::Rt => "Rt".to_string(),
            ChannelType::Lo => "Lo".to_string(),
            ChannelType::Ro => "Ro".to_string(),
        };
        write!(f, "{}", name)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub struct ChannelMap {
    ids: Vec<ChannelType>,
}

impl ChannelMap {
    pub fn new() -> Self {
        ChannelMap { ids: Vec::new() }
    }
    pub fn add_channel(&mut self, ch: ChannelType) {
        self.ids.push(ch);
    }
    pub fn add_channels(&mut self, chs: &[ChannelType]) {
        for i in 0..chs.len() {
            self.ids.push(chs[i]);
        }
    }
    pub fn len(&self) -> usize {
        self.ids.len()
    }
    pub fn get_channel(&self, idx: usize) -> ChannelType {
        self.ids[idx]
    }
    pub fn find_channel_id(&self, t: ChannelType) -> Option<u8> {
        for i in 0..self.ids.len() {
            if self.ids[i] as i32 == t as i32 {
                return Some(i as u8);
            }
        }
        None
    }
    pub fn default_map(count: usize) -> Self {
        use self::ChannelType::*;
        let ids = match count {
            1 => vec![C],
            2 => vec![R, L],
            _ => unimplemented!()
        };

        ChannelMap { ids }
    }
}

pub mod formats {
    use super::*;

    pub const U8: Soniton = Soniton {
        bits: 8,
        be: false,
        packed: false,
        planar: false,
        float: false,
        signed: false,
    };
    pub const S16: Soniton = Soniton {
        bits: 16,
        be: false,
        packed: false,
        planar: false,
        float: false,
        signed: true,
    };
    pub const S32: Soniton = Soniton {
        bits: 32,
        be: false,
        packed: false,
        planar: false,
        float: false,
        signed: true,
    };
    pub const F32: Soniton = Soniton {
        bits: 32,
        be: false,
        packed: false,
        planar: false,
        float: true,
        signed: true,
    };
    pub const F64: Soniton = Soniton {
        bits: 64,
        be: false,
        packed: false,
        planar: false,
        float: true,
        signed: true,
    };
}

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

    #[test]
    fn fmt() {
        println!("{}", formats::S16);
        println!("{}", formats::U8);
        println!("{}", formats::F32);
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 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.

#![cfg(not(target_arch = "wasm32"))]

use std::i16;
use std::mem;
use std::str;

use core::num::flt2dec::MAX_SIG_DIGITS;
use core::num::flt2dec::strategy::grisu::format_exact_opt;
use core::num::flt2dec::strategy::grisu::format_shortest_opt;
use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};

use rand::{self, Rand, XorShiftRng};
use rand::distributions::{IndependentSample, Range};

pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
    match decode(v).1 {
        FullDecoded::Finite(decoded) => decoded,
        full_decoded => panic!("expected finite, got {:?} instead", full_decoded)
    }
}

fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
              V: FnMut(usize) -> Decoded {
    assert!(k <= 1024);

    let mut npassed = 0; // f(x) = Some(g(x))
    let mut nignored = 0; // f(x) = None

    for i in 0..n {
        if (i & 0xfffff) == 0 {
            println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})",
                     i, n, nignored, npassed, i - nignored - npassed);
        }

        let decoded = v(i);
        let mut buf1 = [0; 1024];
        if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
            let mut buf2 = [0; 1024];
            let (len2, e2) = g(&decoded, &mut buf2[..k]);
            if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
                npassed += 1;
            } else {
                println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
                         i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
                                        str::from_utf8(&buf2[..len2]).unwrap(), e2);
            }
        } else {
            nignored += 1;
        }
    }
    println!("{}({}): done, ignored={} passed={} failed={}",
             func, k, nignored, npassed, n - nignored - npassed);
    assert!(nignored + npassed == n,
            "{}({}): {} out of {} values returns an incorrect value!",
            func, k, n - nignored - npassed, n);
    (npassed, nignored)
}

pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
    let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
    iterate("f32_random_equivalence_test", k, n, f, g, |_| {
        let i: u32 = f32_range.ind_sample(&mut rng);
        let x: f32 = unsafe {mem::transmute(i)};
        decode_finite(x)
    });
}

pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
    let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
    iterate("f64_random_equivalence_test", k, n, f, g, |_| {
        let i: u64 = f64_range.ind_sample(&mut rng);
        let x: f64 = unsafe {mem::transmute(i)};
        decode_finite(x)
    });
}

pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
    // so why not simply testing all of them?
    //
    // this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
    // but with `-C opt-level=3 -C lto` this only takes about an hour or so.

    // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges
    let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test",
                                      k, 0x7f7f_ffff, f, g, |i: usize| {
        let x: f32 = unsafe {mem::transmute(i as u32 + 1)};
        decode_finite(x)
    });
    assert_eq!((npassed, nignored), (2121451881, 17643158));
}

#[test]
fn shortest_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
    f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
}

#[test] #[ignore] // it is too expensive
fn shortest_f32_exhaustive_equivalence_test() {
    // it is hard to directly test the optimality of the output, but we can at least test if
    // two different algorithms agree to each other.
    //
    // this reports the progress and the number of f32 values returned `None`.
    // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
    // `done, ignored=17643158 passed=2121451881 failed=0`.

    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}

#[test] #[ignore] // it is too expensive
fn shortest_f64_hard_random_equivalence_test() {
    // this again probably has to use appropriate rustc flags.

    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f64_random_equivalence_test(format_shortest_opt, fallback,
                                         MAX_SIG_DIGITS, 100_000_000);
}

#[test]
fn exact_f32_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
    for k in 1..21 {
        f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
    }
}

#[test]
fn exact_f64_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
    for k in 1..21 {
        f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
    }
}

<commit_msg>Remove some transmutes<commit_after>// Copyright 2017 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.

#![cfg(not(target_arch = "wasm32"))]

use std::i16;
use std::str;

use core::num::flt2dec::MAX_SIG_DIGITS;
use core::num::flt2dec::strategy::grisu::format_exact_opt;
use core::num::flt2dec::strategy::grisu::format_shortest_opt;
use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};

use rand::{self, Rand, XorShiftRng};
use rand::distributions::{IndependentSample, Range};

pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
    match decode(v).1 {
        FullDecoded::Finite(decoded) => decoded,
        full_decoded => panic!("expected finite, got {:?} instead", full_decoded)
    }
}

fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
              V: FnMut(usize) -> Decoded {
    assert!(k <= 1024);

    let mut npassed = 0; // f(x) = Some(g(x))
    let mut nignored = 0; // f(x) = None

    for i in 0..n {
        if (i & 0xfffff) == 0 {
            println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})",
                     i, n, nignored, npassed, i - nignored - npassed);
        }

        let decoded = v(i);
        let mut buf1 = [0; 1024];
        if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
            let mut buf2 = [0; 1024];
            let (len2, e2) = g(&decoded, &mut buf2[..k]);
            if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
                npassed += 1;
            } else {
                println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
                         i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
                                        str::from_utf8(&buf2[..len2]).unwrap(), e2);
            }
        } else {
            nignored += 1;
        }
    }
    println!("{}({}): done, ignored={} passed={} failed={}",
             func, k, nignored, npassed, n - nignored - npassed);
    assert!(nignored + npassed == n,
            "{}({}): {} out of {} values returns an incorrect value!",
            func, k, n - nignored - npassed, n);
    (npassed, nignored)
}

pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
    let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
    iterate("f32_random_equivalence_test", k, n, f, g, |_| {
        let x = f32::from_bits(f32_range.ind_sample(&mut rng));
        decode_finite(x)
    });
}

pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
    let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
    iterate("f64_random_equivalence_test", k, n, f, g, |_| {
        let x = f64::from_bits(f64_range.ind_sample(&mut rng));
        decode_finite(x)
    });
}

pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
        where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
              G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
    // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
    // so why not simply testing all of them?
    //
    // this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
    // but with `-C opt-level=3 -C lto` this only takes about an hour or so.

    // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges
    let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test",
                                      k, 0x7f7f_ffff, f, g, |i: usize| {

        let x = f32::from_bits(i as u32 + 1);
        decode_finite(x)
    });
    assert_eq!((npassed, nignored), (2121451881, 17643158));
}

#[test]
fn shortest_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
    f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
}

#[test] #[ignore] // it is too expensive
fn shortest_f32_exhaustive_equivalence_test() {
    // it is hard to directly test the optimality of the output, but we can at least test if
    // two different algorithms agree to each other.
    //
    // this reports the progress and the number of f32 values returned `None`.
    // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
    // `done, ignored=17643158 passed=2121451881 failed=0`.

    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}

#[test] #[ignore] // it is too expensive
fn shortest_f64_hard_random_equivalence_test() {
    // this again probably has to use appropriate rustc flags.

    use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
    f64_random_equivalence_test(format_shortest_opt, fallback,
                                         MAX_SIG_DIGITS, 100_000_000);
}

#[test]
fn exact_f32_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
    for k in 1..21 {
        f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
    }
}

#[test]
fn exact_f64_random_equivalence_test() {
    use core::num::flt2dec::strategy::dragon::format_exact as fallback;
    for k in 1..21 {
        f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
                                             |d, buf| fallback(d, buf, i16::MIN), k, 1_000);
    }
}

<|endoftext|>


<commit_before>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{Message, TextMessage, MessageAcknowledgement, Envelope};
use std::collections::{HashMap, HashSet};
use std::sync::mpsc;
use std::thread;

#[derive(Debug)]
pub enum Task {
    HandleNetworkEvent(network::Event),
    ScheduleMessageDelivery(Address, TextMessage),
    Shutdown
}

#[derive(Debug)]
pub enum ScheduledTask {
    DeliverMessage(Address, TextMessage),
}

#[derive(Clone, Debug)]
pub enum Event {
    ReceivedTextMessage(TextMessage),
    ReceivedMessageAcknowledgement(MessageAcknowledgement),
    Shutdown
}

pub type TaskSender = mio::Sender<Task>;

pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    pending_deliveries: HashMap<Address, mio::Timeout>,
    delivered: HashMap<Address, usize>,
    acknowledgements: HashMap<Address, MessageAcknowledgement>,
    event_listeners: Vec<mpsc::Sender<Event>>
}

impl Client {
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            pending_deliveries: HashMap::new(),
            delivered: HashMap::new(),
            acknowledgements: HashMap::new(),
            event_listeners: Vec::new()
        }
    }

    pub fn run(mut self, mut network: network::Network) -> TaskSender {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel
                    .send(Task::HandleNetworkEvent(event))
                    .unwrap_or_else(|err| info!("Couldn't handle network event: {:?}", err));
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        info!("Running client at {}", self.address);
        thread::spawn(move || event_loop.run(&mut self).unwrap());
        notify_channel
    }

    pub fn register_event_listener(&mut self, event_listener: mpsc::Sender<Event>) {
        self.event_listeners.push(event_listener);
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(sender, data) => {
                let envelope = messages::decode(data);
                let Envelope { recipient, .. } = envelope;

                match envelope.message {
                    Message::TextMessage(text_message) => {
                        if recipient == self.address {
                            if self.received.insert(text_message.id) {
                                let ack = MessageAcknowledgement::new(text_message.id);
                                let sender = text_message.sender;
                                let event = Event::ReceivedTextMessage(text_message);
                                self.broadcast_event(event);
                                self.deliver_acknowledgement(sender, ack, event_loop);
                            }
                        } else {
                            if let Some(ack) = self.acknowledgements.remove(&text_message.id) {
                                self.deliver_acknowledgement(sender, ack.clone(), event_loop);
                                self.acknowledgements.insert(text_message.id, ack);
                            } else {
                                self.schedule_message_delivery(recipient, text_message, event_loop);
                            }
                        }
                    }
                    Message::MessageAcknowledgement(ack) => {
                        debug!("ack {}", ack.message_id);
                        self.pending_deliveries.remove(&ack.message_id).map(|p| event_loop.clear_timeout(p));

                        if let None = self.acknowledgements.insert(ack.message_id, ack.clone()) {
                            if recipient == self.address {
                                self.broadcast_event(Event::ReceivedMessageAcknowledgement(ack));
                            } else {
                                self.deliver_acknowledgement(recipient, ack, event_loop);
                            }
                        }
                    }
                }
            }
            network::Event::Shutdown => {
                event_loop.shutdown();
                self.broadcast_event(Event::Shutdown);
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let message_id = text_message.id;
        if !self.pending_deliveries.contains_key(&message_id) {
            let delivered = self.delivered.entry(message_id).or_insert(0);
            let delay = (2u64.pow(*delivered as u32) - 1) * 1000;
            debug!("Deliver {} with delay {:?}", message_id, delay);
            let timeout = event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, text_message), delay).unwrap();
            self.pending_deliveries.insert(message_id, timeout);
            *delivered += 1;
        }
    }

    fn deliver_acknowledgement(&mut self, recipient: Address, acknowledgement: MessageAcknowledgement, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            let envelope = acknowledgement.envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode())).unwrap();
        }
    }

    fn deliver_message(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let delivered = self.network_commands.as_ref().map(|commands| {
            let envelope = text_message.clone().envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode()))
        }).unwrap();

        if let Ok(_) = delivered {
            self.pending_deliveries.remove(&text_message.id);
            self.schedule_message_delivery(recipient, text_message, event_loop);
        }
    }

    fn broadcast_event(&self, event: Event) {
        for listener in self.event_listeners.iter() {
            listener.send(event.clone()).expect("Could not broadcast event");
        }
    }

    fn shutdown(&self, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            commands.send(network::OneshotTask::Shutdown).unwrap();
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop),
            Task::Shutdown => self.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message, event_loop)
        }
    }
}
<commit_msg>Type alias client event<commit_after>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{Message, TextMessage, MessageAcknowledgement, Envelope};
use std::collections::{HashMap, HashSet};
use std::sync::mpsc;
use std::thread;

#[derive(Debug)]
pub enum Task {
    HandleNetworkEvent(network::Event),
    ScheduleMessageDelivery(Address, TextMessage),
    Shutdown
}

#[derive(Debug)]
pub enum ScheduledTask {
    DeliverMessage(Address, TextMessage),
}

#[derive(Clone, Debug)]
pub enum Event {
    ReceivedTextMessage(TextMessage),
    ReceivedMessageAcknowledgement(MessageAcknowledgement),
    Shutdown
}

pub type TaskSender = mio::Sender<Task>;
pub type Events = mpsc::Sender<Event>;

pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    pending_deliveries: HashMap<Address, mio::Timeout>,
    delivered: HashMap<Address, usize>,
    acknowledgements: HashMap<Address, MessageAcknowledgement>,
    event_listeners: Vec<mpsc::Sender<Event>>
}

impl Client {
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            pending_deliveries: HashMap::new(),
            delivered: HashMap::new(),
            acknowledgements: HashMap::new(),
            event_listeners: Vec::new()
        }
    }

    pub fn run(mut self, mut network: network::Network) -> TaskSender {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel
                    .send(Task::HandleNetworkEvent(event))
                    .unwrap_or_else(|err| info!("Couldn't handle network event: {:?}", err));
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        info!("Running client at {}", self.address);
        thread::spawn(move || event_loop.run(&mut self).unwrap());
        notify_channel
    }

    pub fn register_event_listener(&mut self, event_listener: Events) {
        self.event_listeners.push(event_listener);
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(sender, data) => {
                let envelope = messages::decode(data);
                let Envelope { recipient, .. } = envelope;

                match envelope.message {
                    Message::TextMessage(text_message) => {
                        if recipient == self.address {
                            if self.received.insert(text_message.id) {
                                let ack = MessageAcknowledgement::new(text_message.id);
                                let sender = text_message.sender;
                                let event = Event::ReceivedTextMessage(text_message);
                                self.broadcast_event(event);
                                self.deliver_acknowledgement(sender, ack, event_loop);
                            }
                        } else {
                            if let Some(ack) = self.acknowledgements.remove(&text_message.id) {
                                self.deliver_acknowledgement(sender, ack.clone(), event_loop);
                                self.acknowledgements.insert(text_message.id, ack);
                            } else {
                                self.schedule_message_delivery(recipient, text_message, event_loop);
                            }
                        }
                    }
                    Message::MessageAcknowledgement(ack) => {
                        debug!("ack {}", ack.message_id);
                        self.pending_deliveries.remove(&ack.message_id).map(|p| event_loop.clear_timeout(p));

                        if let None = self.acknowledgements.insert(ack.message_id, ack.clone()) {
                            if recipient == self.address {
                                self.broadcast_event(Event::ReceivedMessageAcknowledgement(ack));
                            } else {
                                self.deliver_acknowledgement(recipient, ack, event_loop);
                            }
                        }
                    }
                }
            }
            network::Event::Shutdown => {
                event_loop.shutdown();
                self.broadcast_event(Event::Shutdown);
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let message_id = text_message.id;
        if !self.pending_deliveries.contains_key(&message_id) {
            let delivered = self.delivered.entry(message_id).or_insert(0);
            let delay = (2u64.pow(*delivered as u32) - 1) * 1000;
            debug!("Deliver {} with delay {:?}", message_id, delay);
            let timeout = event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, text_message), delay).unwrap();
            self.pending_deliveries.insert(message_id, timeout);
            *delivered += 1;
        }
    }

    fn deliver_acknowledgement(&mut self, recipient: Address, acknowledgement: MessageAcknowledgement, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            let envelope = acknowledgement.envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode())).unwrap();
        }
    }

    fn deliver_message(&mut self, recipient: Address, text_message: TextMessage, event_loop: &mut mio::EventLoop<Client>) {
        let delivered = self.network_commands.as_ref().map(|commands| {
            let envelope = text_message.clone().envelope(recipient);
            commands.send(network::OneshotTask::SendPacket(recipient, envelope.encode()))
        }).unwrap();

        if let Ok(_) = delivered {
            self.pending_deliveries.remove(&text_message.id);
            self.schedule_message_delivery(recipient, text_message, event_loop);
        }
    }

    fn broadcast_event(&self, event: Event) {
        for listener in self.event_listeners.iter() {
            listener.send(event.clone()).expect("Could not broadcast event");
        }
    }

    fn shutdown(&self, _event_loop: &mut mio::EventLoop<Client>) {
        if let Some(ref commands) = self.network_commands {
            commands.send(network::OneshotTask::Shutdown).unwrap();
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop),
            Task::Shutdown => self.shutdown(event_loop)
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message, event_loop)
        }
    }
}
<|endoftext|>


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

//! Dissection of Internet Protocol (IP) packets.
//!
//! This module will eventually contain dissectors for protocols in the IP suite,
//! e.g., `rshark::ip::icmp` and `rshark::ip::tcp`.
//! For now, it only handles IP headers.
//!
//! See [RFC 791](https://tools.ietf.org/html/rfc791).

use {
    Endianness,
    Error,
    NamedValue,
    Result,
    Val,
    raw,
    unsigned,
};

pub fn dissect(data : &[u8]) -> Result {
    if data.len() < 20 {
        return Err(Error::Underflow { expected: 20, have: data.len(),
            message: "An IP packet must be at least 20 B".to_string() })
    }

    let mut values:Vec<NamedValue> = vec![];

    // IP version (should be "4")
    let version = data[0] >> 4;
    values.push(("Version".to_string(), Ok(Val::Unsigned(version as u64))));

    // Internet Header Length (IHL): number of 32b words in header
    let words = data[0] & 0x0f;
    values.push(("IHL".to_string(), Ok(Val::Unsigned(words as u64))));

    // Differentiated Services Code Point (DSCP): RFC 2474
    let dscp = data[1] >> 2;
    values.push(("DSCP".to_string(), Ok(Val::Unsigned(dscp as u64))));

    // Explicit Congestion Notification (ECN): RFC 3168
    let ecn = data[1] & 0x03;
    values.push(("ECN".to_string(), Ok(Val::Unsigned(ecn as u64))));

    // Total length (including header)
    let length = unsigned(&data[2..4], Endianness::BigEndian);
    values.push(("Length".to_string(), length.map(|v| Val::Unsigned(v))));

    // Identification (of datagraph fragments): RFC 6864
    values.push(("Identification".to_string(), Ok(Val::Unsigned(data[8] as u64))));

    // Protocol number (assigned by IANA)
    let protocol = data[9];
    values.push(("Protocol".to_string(), Ok(Val::Unsigned(protocol as u64))));

    // Header checksum
    values.push(("Checksum".to_string(), Ok(Val::Bytes(data[10..12].to_vec()))));

    // Source and destination addresses
    let source = &data[12..16];
    values.push(("Source".to_string(), Ok(Val::Address {
        bytes: source.to_vec(),
        encoded: source.iter().map(|b| b.to_string()).collect::<Vec<_>>().connect("."),
    })));

    let dest = &data[16..20];
    values.push(("Destination".to_string(), Ok(Val::Address {
        bytes: dest.to_vec(),
        encoded: dest.iter().map(|b| b.to_string()).collect::<Vec<_>>().connect("."),
    })));

    // Parse the remainder according to the specified protocol.
    let remainder = &data[20..];
    let dissect_pdu = match protocol {
        // TODO: UDP, TCP, etc.
        _ => raw,
    };

    values.push(("Protocol Data".to_string(), dissect_pdu(remainder)));

    Ok(Val::Object(values))
}
<commit_msg>Change deprecated `connect` to `join`.<commit_after>/*
 * Copyright 2015 Jonathan Anderson
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

//! Dissection of Internet Protocol (IP) packets.
//!
//! This module will eventually contain dissectors for protocols in the IP suite,
//! e.g., `rshark::ip::icmp` and `rshark::ip::tcp`.
//! For now, it only handles IP headers.
//!
//! See [RFC 791](https://tools.ietf.org/html/rfc791).

use {
    Endianness,
    Error,
    NamedValue,
    Result,
    Val,
    raw,
    unsigned,
};

pub fn dissect(data : &[u8]) -> Result {
    if data.len() < 20 {
        return Err(Error::Underflow { expected: 20, have: data.len(),
            message: "An IP packet must be at least 20 B".to_string() })
    }

    let mut values:Vec<NamedValue> = vec![];

    // IP version (should be "4")
    let version = data[0] >> 4;
    values.push(("Version".to_string(), Ok(Val::Unsigned(version as u64))));

    // Internet Header Length (IHL): number of 32b words in header
    let words = data[0] & 0x0f;
    values.push(("IHL".to_string(), Ok(Val::Unsigned(words as u64))));

    // Differentiated Services Code Point (DSCP): RFC 2474
    let dscp = data[1] >> 2;
    values.push(("DSCP".to_string(), Ok(Val::Unsigned(dscp as u64))));

    // Explicit Congestion Notification (ECN): RFC 3168
    let ecn = data[1] & 0x03;
    values.push(("ECN".to_string(), Ok(Val::Unsigned(ecn as u64))));

    // Total length (including header)
    let length = unsigned(&data[2..4], Endianness::BigEndian);
    values.push(("Length".to_string(), length.map(|v| Val::Unsigned(v))));

    // Identification (of datagraph fragments): RFC 6864
    values.push(("Identification".to_string(), Ok(Val::Unsigned(data[8] as u64))));

    // Protocol number (assigned by IANA)
    let protocol = data[9];
    values.push(("Protocol".to_string(), Ok(Val::Unsigned(protocol as u64))));

    // Header checksum
    values.push(("Checksum".to_string(), Ok(Val::Bytes(data[10..12].to_vec()))));

    // Source and destination addresses
    let source = &data[12..16];
    values.push(("Source".to_string(), Ok(Val::Address {
        bytes: source.to_vec(),
        encoded: source.iter().map(|b| b.to_string()).collect::<Vec<_>>().join("."),
    })));

    let dest = &data[16..20];
    values.push(("Destination".to_string(), Ok(Val::Address {
        bytes: dest.to_vec(),
        encoded: dest.iter().map(|b| b.to_string()).collect::<Vec<_>>().join("."),
    })));

    // Parse the remainder according to the specified protocol.
    let remainder = &data[20..];
    let dissect_pdu = match protocol {
        // TODO: UDP, TCP, etc.
        _ => raw,
    };

    values.push(("Protocol Data".to_string(), dissect_pdu(remainder)));

    Ok(Val::Object(values))
}
<|endoftext|>


<commit_before>use super::Config;
use super::EditMode;
use super::KeyPress;
use super::RawReader;
use super::Result;

//#[derive(Clone)]
pub enum Cmd {
    Abort, // Miscellaneous Command
    AcceptLine, // Command For History
    BackwardChar, // Command For Moving
    BackwardDeleteChar, // Command For Text
    BackwardKillWord, // Command For Killing
    BackwardWord, // Command For Moving
    BeginningOfHistory, // Command For History
    BeginningOfLine, // Command For Moving
    CapitalizeWord, // Command For Text
    CharacterSearch(bool), // Miscellaneous Command (TODO Move right to the next occurance of c)
    CharacterSearchBackward(bool), /* Miscellaneous Command (TODO Move left to the previous occurance of c) */
    ClearScreen, // Command For Moving
    Complete, // Command For Completion
    DeleteChar, // Command For Text
    DowncaseWord, // Command For Text
    EndOfFile, // Command For Text
    EndOfHistory, // Command For History
    EndOfLine, // Command For Moving
    ForwardChar, // Command For Moving
    ForwardSearchHistory, // Command For History
    ForwardWord, // Command For Moving
    KillLine, // Command For Killing
    KillWholeLine, // Command For Killing (TODO Delete current line)
    KillWord, // Command For Killing
    NextHistory, // Command For History
    Noop,
    PreviousHistory, // Command For History
    QuotedInsert, // Command For Text
    Replace, // TODO DeleteChar + SelfInsert
    ReverseSearchHistory, // Command For History
    SelfInsert, // Command For Text
    TransposeChars, // Command For Text
    TransposeWords, // Command For Text
    Unknown,
    UnixLikeDiscard, // Command For Killing
    UnixWordRubout, // Command For Killing
    UpcaseWord, // Command For Text
    Yank, // Command For Killing
    YankPop, // Command For Killing
}

// TODO numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
pub struct EditState {
    mode: EditMode,
    // TODO Validate Vi Command, Insert, Visual mode
    insert: bool, // vi only ?
}

impl EditState {
    pub fn new(config: &Config) -> EditState {
        EditState {
            mode: config.edit_mode(),
            insert: true,
        }
    }

    pub fn next_cmd<R: RawReader>(&mut self,
                                  rdr: &mut R,
                                  config: &Config)
                                  -> Result<(KeyPress, Cmd)> {
        match self.mode {
            EditMode::Emacs => self.emacs(rdr, config),
            EditMode::Vi if self.insert => self.vi_insert(rdr, config),
            EditMode::Vi => self.vi_command(rdr, config),
        }
    }

    fn emacs<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Esc => Cmd::Abort, // TODO Validate
            KeyPress::Ctrl('A') => Cmd::BeginningOfLine,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Ctrl('B') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            // KeyPress::Ctrl('D') if s.line.is_empty() => Cmd::EndOfFile,
            KeyPress::Ctrl('D') => Cmd::DeleteChar,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('E') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('F') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Meta('\x08') => Cmd::BackwardKillWord,
            KeyPress::Meta('\x7f') => Cmd::BackwardKillWord,
            // KeyPress::Meta('-') => { // digit-argument
            // }
            // KeyPress::Meta('0'...'9') => { // digit-argument
            // }
            KeyPress::Meta('<') => Cmd::BeginningOfHistory,
            KeyPress::Meta('>') => Cmd::EndOfHistory,
            KeyPress::Meta('B') => Cmd::BackwardWord,
            KeyPress::Meta('C') => Cmd::CapitalizeWord,
            KeyPress::Meta('D') => Cmd::KillWord,
            KeyPress::Meta('F') => Cmd::ForwardWord,
            KeyPress::Meta('L') => Cmd::DowncaseWord,
            KeyPress::Meta('T') => Cmd::TransposeWords,
            KeyPress::Meta('U') => Cmd::UpcaseWord,
            KeyPress::Meta('Y') => Cmd::YankPop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_command<R: RawReader>(&mut self,
                                rdr: &mut R,
                                config: &Config)
                                -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char('$') => Cmd::EndOfLine,
            // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket
            KeyPress::Char('0') => Cmd::BeginningOfLine, // vi-zero: Vi move to the beginning of line.
            // KeyPress::Char('1'...'9') => Cmd::???, // vi-arg-digit
            KeyPress::Char('^') => Cmd::BeginningOfLine, // TODO Move to the first non-blank character of line.
            KeyPress::Char('a') => {
                // vi-append-mode: Vi enter insert mode after the cursor.
                self.insert = true;
                Cmd::ForwardChar
            }
            KeyPress::Char('A') => {
                // vi-append-eol: Vi enter insert mode at end of line.
                self.insert = true;
                Cmd::EndOfLine
            }
            KeyPress::Char('b') => Cmd::BackwardWord,
            // TODO KeyPress::Char('B') => Cmd::???, Move one non-blank word left.
            KeyPress::Char('c') => {
                self.insert = true;
                let mvt = try!(rdr.next_key(config.keyseq_timeout()));
                match mvt {
                    KeyPress::Char('$') => Cmd::KillLine, // vi-change-to-eol: Vi change to end of line.
                    KeyPress::Char('0') => Cmd::UnixLikeDiscard,
                    KeyPress::Char('c') => Cmd::KillWholeLine,
                    // TODO KeyPress::Char('f') => ???,
                    // TODO KeyPress::Char('F') => ???,
                    KeyPress::Char('h') => Cmd::BackwardDeleteChar,
                    KeyPress::Char('l') => Cmd::DeleteChar,
                    KeyPress::Char(' ') => Cmd::DeleteChar,
                    // TODO KeyPress::Char('t') => ???,
                    // TODO KeyPress::Char('T') => ???,
                    KeyPress::Char('w') => Cmd::KillWord,
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('C') => {
                self.insert = true;
                Cmd::KillLine
            }
            KeyPress::Char('d') => {
                let mvt = try!(rdr.next_key(config.keyseq_timeout()));
                match mvt {
                    KeyPress::Char('$') => Cmd::KillLine,
                    KeyPress::Char('0') => Cmd::UnixLikeDiscard, // vi-kill-line-prev: Vi cut from beginning of line to cursor.
                    KeyPress::Char('d') => Cmd::KillWholeLine,
                    // TODO KeyPress::Char('f') => ???,
                    // TODO KeyPress::Char('F') => ???,
                    KeyPress::Char('h') => Cmd::BackwardDeleteChar, // vi-delete-prev-char: Vi move to previous character (backspace).
                    KeyPress::Char('l') => Cmd::DeleteChar,
                    KeyPress::Char(' ') => Cmd::DeleteChar,
                    // TODO KeyPress::Char('t') => ???,
                    // TODO KeyPress::Char('T') => ???,
                    KeyPress::Char('w') => Cmd::KillWord,
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('D') => Cmd::KillLine,
            // TODO KeyPress::Char('e') => Cmd::???, vi-to-end-word: Vi move to the end of the current word. Move to the end of the current word.
            // TODO KeyPress::Char('E') => Cmd::???, vi-end-word: Vi move to the end of the current space delimited word. Move to the end of the current non-blank word.
            KeyPress::Char('i') => {
                // vi-insert: Vi enter insert mode.
                self.insert = true;
                Cmd::Noop
            }
            KeyPress::Char('I') => {
                // vi-insert-at-bol: Vi enter insert mode at the beginning of line.
                self.insert = true;
                Cmd::BeginningOfLine
            }
            KeyPress::Char('f') => {
                // vi-next-char: Vi move to the character specified next.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearch(false))),
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('F') => {
                // vi-prev-char: Vi move to the character specified previous.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearchBackward(false))),
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n
            KeyPress::Char('p') => Cmd::Yank, // vi-paste-next: Vi paste previous deletion to the right of the cursor.
            KeyPress::Char('P') => Cmd::Yank, // vi-paste-prev: Vi paste previous deletion to the left of the cursor. TODO Insert the yanked text before the cursor.
            KeyPress::Char('r') => {
                // vi-replace-char: Vi replace character under the cursor with the next character typed.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::Replace)),
                    KeyPress::Esc => Cmd::Noop,
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('R') => Cmd::???, vi-replace-mode: Vi enter replace mode. Replaces characters under the cursor. (overwrite-mode)
            KeyPress::Char('s') => {
                // vi-substitute-char: Vi replace character under the cursor and enter insert mode.
                self.insert = true;
                Cmd::DeleteChar
            }
            KeyPress::Char('S') => {
                // vi-substitute-line: Vi substitute entire line.
                self.insert = true;
                Cmd::KillWholeLine
            }
            KeyPress::Char('t') => {
                // vi-to-next-char: Vi move up to the character specified next.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearchBackward(true))),
                    _ => Cmd::Unknown,
                }
            }
            KeyPress::Char('T') => {
                // vi-to-prev-char: Vi move up to the character specified previous.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::CharacterSearch(true))),
                    _ => Cmd::Unknown,
                }
            }
            // KeyPress::Char('U') => Cmd::???, // revert-line
            KeyPress::Char('w') => Cmd::ForwardWord, // vi-next-word: Vi move to the next word.
            // TODO KeyPress::Char('W') => Cmd::???, // vi-next-space-word: Vi move to the next space delimited word. Move one non-blank word right.
            KeyPress::Char('x') => Cmd::DeleteChar, // vi-delete: TODO move backward if eol
            KeyPress::Char('X') => Cmd::BackwardDeleteChar,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Char('h') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardChar,
            KeyPress::Char('l') => Cmd::ForwardChar,
            KeyPress::Char(' ') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Char('+') => Cmd::NextHistory,
            KeyPress::Char('j') => Cmd::NextHistory,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Char('-') => Cmd::PreviousHistory,
            KeyPress::Char('k') => Cmd::PreviousHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => Cmd::Noop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile, // vi-eof-maybe
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::UnixWordRubout,
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => {
                // vi-movement-mode/vi-command-mode: Vi enter command mode (use alternative key bindings).
                self.insert = false;
                Cmd::Noop
            }
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }
}
<commit_msg>Second draft for vi mode support (#94)<commit_after>use super::Config;
use super::EditMode;
use super::KeyPress;
use super::RawReader;
use super::Result;

//#[derive(Clone)]
pub enum Cmd {
    Abort, // Miscellaneous Command
    AcceptLine, // Command For History
    BackwardChar, // Command For Moving
    BackwardDeleteChar, // Command For Text
    BackwardKillWord(Word), // Command For Killing
    BackwardWord(Word), // Command For Moving
    BeginningOfHistory, // Command For History
    BeginningOfLine, // Command For Moving
    CapitalizeWord, // Command For Text
    ClearScreen, // Command For Moving
    Complete, // Command For Completion
    DeleteChar, // Command For Text
    DowncaseWord, // Command For Text
    EndOfFile, // Command For Text
    EndOfHistory, // Command For History
    EndOfLine, // Command For Moving
    ForwardChar, // Command For Moving
    ForwardSearchHistory, // Command For History
    ForwardWord(Word), // Command For Moving
    KillLine, // Command For Killing
    KillWholeLine, // Command For Killing (TODO Delete current line)
    KillWord(Word), // Command For Killing
    NextHistory, // Command For History
    Noop, // TODO
    PreviousHistory, // Command For History
    QuotedInsert, // Command For Text
    Replace, // TODO DeleteChar + SelfInsert
    ReverseSearchHistory, // Command For History
    SelfInsert, // Command For Text
    TransposeChars, // Command For Text
    TransposeWords, // Command For Text
    Unknown,
    UnixLikeDiscard, // Command For Killing
    // UnixWordRubout, // = KillWord(Word::BigWord) Command For Killing
    UpcaseWord, // Command For Text
    ViCharSearch(CharSearch), // TODO
    ViEndWord(Word), // TODO
    ViKillTo(CharSearch), // TODO
    Yank, // Command For Killing
    YankPop, // Command For Killing
}

pub enum Word {
    // non-blanks characters
    BigWord,
    // alphanumeric characters
    Word,
    // alphanumeric (and '_') characters
    ViWord,
}

pub enum CharSearch {
    Forward(char),
    // until
    ForwardBefore(char),
    Backward(char),
    // until
    BackwardAfter(char),
}

// TODO numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
pub struct EditState {
    mode: EditMode,
    // Vi Command/Alternate, Insert/Input mode
    insert: bool, // vi only ?
}

impl EditState {
    pub fn new(config: &Config) -> EditState {
        EditState {
            mode: config.edit_mode(),
            insert: true,
        }
    }

    pub fn next_cmd<R: RawReader>(&mut self,
                                  rdr: &mut R,
                                  config: &Config)
                                  -> Result<(KeyPress, Cmd)> {
        match self.mode {
            EditMode::Emacs => self.emacs(rdr, config),
            EditMode::Vi if self.insert => self.vi_insert(rdr, config),
            EditMode::Vi => self.vi_command(rdr, config),
        }
    }

    fn emacs<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Esc => Cmd::Abort, // TODO Validate
            KeyPress::Ctrl('A') => Cmd::BeginningOfLine,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Ctrl('B') => Cmd::BackwardChar,
            KeyPress::Left => Cmd::BackwardChar,
            // KeyPress::Ctrl('D') if s.line.is_empty() => Cmd::EndOfFile,
            KeyPress::Ctrl('D') => Cmd::DeleteChar,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('E') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Ctrl('F') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Meta('\x08') => Cmd::BackwardKillWord(Word::Word),
            KeyPress::Meta('\x7f') => Cmd::BackwardKillWord(Word::Word),
            // KeyPress::Meta('-') => { // digit-argument
            // }
            // KeyPress::Meta('0'...'9') => { // digit-argument
            // }
            KeyPress::Meta('<') => Cmd::BeginningOfHistory,
            KeyPress::Meta('>') => Cmd::EndOfHistory,
            KeyPress::Meta('B') => Cmd::BackwardWord(Word::Word),
            KeyPress::Meta('C') => Cmd::CapitalizeWord,
            KeyPress::Meta('D') => Cmd::KillWord(Word::Word),
            KeyPress::Meta('F') => Cmd::ForwardWord(Word::Word),
            KeyPress::Meta('L') => Cmd::DowncaseWord,
            KeyPress::Meta('T') => Cmd::TransposeWords,
            KeyPress::Meta('U') => Cmd::UpcaseWord,
            KeyPress::Meta('Y') => Cmd::YankPop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_command<R: RawReader>(&mut self,
                                rdr: &mut R,
                                config: &Config)
                                -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char('$') => Cmd::EndOfLine,
            KeyPress::End => Cmd::EndOfLine,
            // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket
            KeyPress::Char('0') => Cmd::BeginningOfLine, // vi-zero: Vi move to the beginning of line.
            KeyPress::Home => Cmd::BeginningOfLine,
            // KeyPress::Char('1'...'9') => Cmd::???, // vi-arg-digit
            KeyPress::Char('^') => Cmd::BeginningOfLine, // vi-first-print TODO Move to the first non-blank character of line.
            KeyPress::Char('a') => {
                // vi-append-mode: Vi enter insert mode after the cursor.
                self.insert = true;
                Cmd::ForwardChar
            }
            KeyPress::Char('A') => {
                // vi-append-eol: Vi enter insert mode at end of line.
                self.insert = true;
                Cmd::EndOfLine
            }
            KeyPress::Char('b') => Cmd::BackwardWord(Word::ViWord), // vi-prev-word
            KeyPress::Char('B') => Cmd::BackwardWord(Word::BigWord),
            KeyPress::Char('c') => {
                self.insert = true;
                try!(self.vi_delete_motion(rdr, config, key))
            }
            KeyPress::Char('C') => {
                self.insert = true;
                Cmd::KillLine
            }
            KeyPress::Char('d') => try!(self.vi_delete_motion(rdr, config, key)),
            KeyPress::Char('D') => Cmd::KillLine,
            KeyPress::Char('e') => Cmd::ViEndWord(Word::ViWord),
            KeyPress::Char('E') => Cmd::ViEndWord(Word::BigWord),
            KeyPress::Char('i') => {
                // vi-insertion-mode
                self.insert = true;
                Cmd::Noop
            }
            KeyPress::Char('I') => {
                // vi-insert-beg
                self.insert = true;
                Cmd::BeginningOfLine
            }
            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
                // vi-char-search
                let cs = try!(self.vi_char_search(rdr, config, c));
                match cs {
                    Some(cs) => Cmd::ViCharSearch(cs),
                    None => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n
            KeyPress::Char('p') => Cmd::Yank, // vi-put
            KeyPress::Char('P') => Cmd::Yank, // vi-put TODO Insert the yanked text before the cursor.
            KeyPress::Char('r') => {
                // vi-replace-char: Vi replace character under the cursor with the next character typed.
                let ch = try!(rdr.next_key(config.keyseq_timeout()));
                match ch {
                    KeyPress::Char(_) => return Ok((ch, Cmd::Replace)),
                    KeyPress::Esc => Cmd::Noop,
                    _ => Cmd::Unknown,
                }
            }
            // TODO KeyPress::Char('R') => Cmd::???, vi-replace-mode: Vi enter replace mode. Replaces characters under the cursor. (overwrite-mode)
            KeyPress::Char('s') => {
                // vi-substitute-char: Vi replace character under the cursor and enter insert mode.
                self.insert = true;
                Cmd::DeleteChar
            }
            KeyPress::Char('S') => {
                // vi-substitute-line: Vi substitute entire line.
                self.insert = true;
                Cmd::KillWholeLine
            }
            // KeyPress::Char('U') => Cmd::???, // revert-line
            KeyPress::Char('w') => Cmd::ForwardWord(Word::ViWord), // vi-next-word
            KeyPress::Char('W') => Cmd::ForwardWord(Word::BigWord), // vi-next-word
            KeyPress::Char('x') => Cmd::DeleteChar, // vi-delete: TODO move backward if eol
            KeyPress::Char('X') => Cmd::BackwardDeleteChar, // vi-rubout
            // KeyPress::Char('y') => Cmd::???, // vi-yank-to
            // KeyPress::Char('Y') => Cmd::???, // vi-yank-to
            KeyPress::Char('h') => Cmd::BackwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardChar,
            KeyPress::Backspace => Cmd::BackwardChar, // TODO Validate
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile,
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::Ctrl('G') => Cmd::Abort,
            KeyPress::Char('l') => Cmd::ForwardChar,
            KeyPress::Char(' ') => Cmd::ForwardChar,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('L') => Cmd::ClearScreen,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Char('+') => Cmd::NextHistory,
            KeyPress::Char('j') => Cmd::NextHistory,
            KeyPress::Ctrl('N') => Cmd::NextHistory,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Char('-') => Cmd::PreviousHistory,
            KeyPress::Char('k') => Cmd::PreviousHistory,
            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('K') => Cmd::KillLine,
            KeyPress::Ctrl('Q') => Cmd::QuotedInsert, // most terminals override Ctrl+Q to resume execution
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => Cmd::Noop,
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, config: &Config) -> Result<(KeyPress, Cmd)> {
        let key = try!(rdr.next_key(config.keyseq_timeout()));
        let cmd = match key {
            KeyPress::Char(_) => Cmd::SelfInsert,
            KeyPress::Home => Cmd::BeginningOfLine,
            KeyPress::Left => Cmd::BackwardChar,
            KeyPress::Ctrl('D') => Cmd::EndOfFile, // vi-eof-maybe
            KeyPress::Delete => Cmd::DeleteChar,
            KeyPress::End => Cmd::EndOfLine,
            KeyPress::Right => Cmd::ForwardChar,
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Tab => Cmd::Complete,
            KeyPress::Ctrl('J') => Cmd::AcceptLine,
            KeyPress::Enter => Cmd::AcceptLine,
            KeyPress::Down => Cmd::NextHistory,
            KeyPress::Up => Cmd::PreviousHistory,
            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory,
            KeyPress::Ctrl('T') => Cmd::TransposeChars,
            KeyPress::Ctrl('U') => Cmd::UnixLikeDiscard,
            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
            KeyPress::Ctrl('W') => Cmd::KillWord(Word::BigWord),
            KeyPress::Ctrl('Y') => Cmd::Yank,
            KeyPress::Esc => {
                // vi-movement-mode/vi-command-mode: Vi enter command mode (use alternative key bindings).
                self.insert = false;
                Cmd::Noop
            }
            _ => Cmd::Unknown,
        };
        Ok((key, cmd))
    }

    fn vi_delete_motion<R: RawReader>(&mut self,
                                      rdr: &mut R,
                                      config: &Config,
                                      key: KeyPress)
                                      -> Result<Cmd> {
        let mvt = try!(rdr.next_key(config.keyseq_timeout()));
        Ok(match mvt {
            KeyPress::Char('$') => Cmd::KillLine, // vi-change-to-eol: Vi change to end of line.
            KeyPress::Char('0') => Cmd::UnixLikeDiscard, // vi-kill-line-prev: Vi cut from beginning of line to cursor.
            KeyPress::Char('b') => Cmd::BackwardKillWord(Word::ViWord),
            KeyPress::Char('B') => Cmd::BackwardKillWord(Word::BigWord),
            x if x == key => Cmd::KillWholeLine,
            KeyPress::Char('e') => Cmd::KillWord(Word::ViWord),
            KeyPress::Char('E') => Cmd::KillWord(Word::BigWord),
            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
                let cs = try!(self.vi_char_search(rdr, config, c));
                match cs {
                    Some(cs) => Cmd::ViKillTo(cs),
                    None => Cmd::Unknown,
                }
            }
            KeyPress::Char('h') => Cmd::BackwardDeleteChar, // vi-delete-prev-char: Vi move to previous character (backspace).
            KeyPress::Ctrl('H') => Cmd::BackwardDeleteChar,
            KeyPress::Backspace => Cmd::BackwardDeleteChar,
            KeyPress::Char('l') => Cmd::DeleteChar,
            KeyPress::Char(' ') => Cmd::DeleteChar,
            KeyPress::Char('w') => Cmd::KillWord(Word::ViWord),
            KeyPress::Char('W') => Cmd::KillWord(Word::BigWord),
            _ => Cmd::Unknown,
        })
    }

    fn vi_char_search<R: RawReader>(&mut self,
                                    rdr: &mut R,
                                    config: &Config,
                                    cmd: char)
                                    -> Result<Option<CharSearch>> {
        let ch = try!(rdr.next_key(config.keyseq_timeout()));
        Ok(match ch {
            KeyPress::Char(ch) => {
                Some(match cmd {
                    'f' => CharSearch::Forward(ch),
                    't' => CharSearch::ForwardBefore(ch),
                    'F' => CharSearch::Backward(ch),
                    'T' => CharSearch::BackwardAfter(ch),
                    _ => unreachable!(),
                })
            }
            _ => None,
        })
    }
}
<|endoftext|>