<commit_before>use crate::ffi::OsStr;
use crate::mem;
use crate::path::Prefix;

#[cfg(test)]
mod tests;

pub const MAIN_SEP_STR: &str = "\\";
pub const MAIN_SEP: char = '\\';

fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
    unsafe { mem::transmute(s) }
}
unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
    mem::transmute(s)
}

#[inline]
pub fn is_sep_byte(b: u8) -> bool {
    b == b'/' || b == b'\\'
}

#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
    b == b'\\'
}

// In most DOS systems, it is not possible to have more than 26 drive letters.
// See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
pub fn is_valid_drive_letter(disk: u8) -> bool {
    disk.is_ascii_alphabetic()
}

pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
    use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
    unsafe {
        // The unsafety here stems from converting between &OsStr and &[u8]
        // and back. This is safe to do because (1) we only look at ASCII
        // contents of the encoding and (2) new &OsStr values are produced
        // only from ASCII-bounded slices of existing &OsStr values.
        let path = os_str_as_u8_slice(path);

        // \\
        if let Some(path) = path.strip_prefix(br"\\") {
            // \\?\
            if let Some(path) = path.strip_prefix(br"?\") {
                // \\?\UNC\server\share
                if let Some(path) = path.strip_prefix(br"UNC\") {
                    let (server, share) = match get_first_two_components(path, is_verbatim_sep) {
                        Some((server, share)) => {
                            (u8_slice_as_os_str(server), u8_slice_as_os_str(share))
                        }
                        None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])),
                    };
                    return Some(VerbatimUNC(server, share));
                } else {
                    // \\?\path
                    match path {
                        // \\?\C:\path
                        [c, b':', b'\\', ..] if is_valid_drive_letter(*c) => {
                            return Some(VerbatimDisk(c.to_ascii_uppercase()));
                        }
                        // \\?\cat_pics
                        _ => {
                            let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
                            let slice = &path[..idx];
                            return Some(Verbatim(u8_slice_as_os_str(slice)));
                        }
                    }
                }
            } else if let Some(path) = path.strip_prefix(b".\\") {
                // \\.\COM42
                let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
                let slice = &path[..idx];
                return Some(DeviceNS(u8_slice_as_os_str(slice)));
            }
            match get_first_two_components(path, is_sep_byte) {
                Some((server, share)) if !server.is_empty() && !share.is_empty() => {
                    // \\server\share
                    return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)));
                }
                _ => {}
            }
        } else if let [c, b':', ..] = path {
            // C:
            if is_valid_drive_letter(*c) {
                return Some(Disk(c.to_ascii_uppercase()));
            }
        }
        return None;
    }
}

/// Returns the first two path components with predicate `f`.
///
/// The two components returned will be use by caller
/// to construct `VerbatimUNC` or `UNC` Windows path prefix.
///
/// Returns [`None`] if there are no separators in path.
fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
    let idx = path.iter().position(|&x| f(x))?;
    // Panic safe
    // The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index.
    let (first, path) = (&path[..idx], &path[idx + 1..]);
    let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len());
    let second = &path[..idx];
    Some((first, second))
}
<commit_msg>Prefer empty OsStr over unsafe cast from [u8]<commit_after>use crate::ffi::OsStr;
use crate::mem;
use crate::path::Prefix;

#[cfg(test)]
mod tests;

pub const MAIN_SEP_STR: &str = "\\";
pub const MAIN_SEP: char = '\\';

fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
    unsafe { mem::transmute(s) }
}
unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
    mem::transmute(s)
}

#[inline]
pub fn is_sep_byte(b: u8) -> bool {
    b == b'/' || b == b'\\'
}

#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
    b == b'\\'
}

// In most DOS systems, it is not possible to have more than 26 drive letters.
// See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
pub fn is_valid_drive_letter(disk: u8) -> bool {
    disk.is_ascii_alphabetic()
}

pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
    use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
    unsafe {
        // The unsafety here stems from converting between &OsStr and &[u8]
        // and back. This is safe to do because (1) we only look at ASCII
        // contents of the encoding and (2) new &OsStr values are produced
        // only from ASCII-bounded slices of existing &OsStr values.
        let path = os_str_as_u8_slice(path);

        // \\
        if let Some(path) = path.strip_prefix(br"\\") {
            // \\?\
            if let Some(path) = path.strip_prefix(br"?\") {
                // \\?\UNC\server\share
                if let Some(path) = path.strip_prefix(br"UNC\") {
                    let (server, share) = match get_first_two_components(path, is_verbatim_sep) {
                        Some((server, share)) => {
                            (u8_slice_as_os_str(server), u8_slice_as_os_str(share))
                        }
                        None => (u8_slice_as_os_str(path), OsStr::new("")),
                    };
                    return Some(VerbatimUNC(server, share));
                } else {
                    // \\?\path
                    match path {
                        // \\?\C:\path
                        [c, b':', b'\\', ..] if is_valid_drive_letter(*c) => {
                            return Some(VerbatimDisk(c.to_ascii_uppercase()));
                        }
                        // \\?\cat_pics
                        _ => {
                            let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
                            let slice = &path[..idx];
                            return Some(Verbatim(u8_slice_as_os_str(slice)));
                        }
                    }
                }
            } else if let Some(path) = path.strip_prefix(b".\\") {
                // \\.\COM42
                let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
                let slice = &path[..idx];
                return Some(DeviceNS(u8_slice_as_os_str(slice)));
            }
            match get_first_two_components(path, is_sep_byte) {
                Some((server, share)) if !server.is_empty() && !share.is_empty() => {
                    // \\server\share
                    return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)));
                }
                _ => {}
            }
        } else if let [c, b':', ..] = path {
            // C:
            if is_valid_drive_letter(*c) {
                return Some(Disk(c.to_ascii_uppercase()));
            }
        }
        return None;
    }
}

/// Returns the first two path components with predicate `f`.
///
/// The two components returned will be use by caller
/// to construct `VerbatimUNC` or `UNC` Windows path prefix.
///
/// Returns [`None`] if there are no separators in path.
fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
    let idx = path.iter().position(|&x| f(x))?;
    // Panic safe
    // The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index.
    let (first, path) = (&path[..idx], &path[idx + 1..]);
    let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len());
    let second = &path[..idx];
    Some((first, second))
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::thread;
use std::result::Result;

use action::ActionHandlers;
use super::{config, Command, CommandResult, Context, Dispatcher, Event, Message, Timer};

const TIMER_STEP: u32 = 100;

pub struct Correlator {
    action_handlers: ActionHandlers,
    dispatcher_input_channel: mpsc::Sender<Command>,
    dispatcher_output_channel: mpsc::Receiver<CommandResult>,
    dispatcher_thread_handle: thread::JoinHandle<()>
}

impl Correlator {
    pub fn new(contexts: Vec<config::Context>, action_handlers: ActionHandlers) -> Correlator {
        let (dispatcher_input_channel, rx) = mpsc::channel();
        let (dispatcher_output_channel_tx, dispatcher_output_channel_rx) = mpsc::channel();
        let _ = Timer::from_chan(TIMER_STEP, dispatcher_input_channel.clone());

        let handle = thread::spawn(move || {
            let mut dispatcher = Dispatcher::new(contexts, dispatcher_output_channel_tx);
            dispatcher.start_loop(rx);
        });

        Correlator {
            action_handlers: action_handlers,
            dispatcher_input_channel: dispatcher_input_channel,
            dispatcher_output_channel: dispatcher_output_channel_rx,
            dispatcher_thread_handle: handle
        }
    }

    pub fn push_message(&mut self, message: Message) -> Result<(), mpsc::SendError<Command>> {
        self.consume_results();
        self.dispatcher_input_channel.send(Command::Dispatch(Event::Message(message)))
    }

    fn consume_results(&mut self) {
        for i in self.dispatcher_output_channel.try_recv() {
            if let CommandResult::Dispatch(result) = i {
                self.action_handlers.handle(result);
            }
        }
    }

    fn stop_dispatcher(&mut self) {
        let _ = self.dispatcher_input_channel.send(Command::Exit);
        let mut exit_num = 0;
        loop {
            let value = self.dispatcher_output_channel.recv();
            match value {
                Ok(value) => {
                    match value {
                        CommandResult::Dispatch(result) => self.action_handlers.handle(result),
                        CommandResult::Exit => {
                            let _ = self.dispatcher_input_channel.send(Command::Exit);
                            exit_num += 1;
                            if exit_num >= 1 {
                                break;
                            }
                        }
                    }
                },
                Err(_) => {}
            }
        }
    }

    pub fn stop(mut self) -> thread::Result<()> {
        self.consume_results();
        self.stop_dispatcher();
        self.dispatcher_thread_handle.join()
    }
}
<commit_msg>Correlator: split stop_dispatcher() function into smaller pieces<commit_after>use std::sync::mpsc;
use std::thread;
use std::result::Result;

use action::ActionHandlers;
use super::{config, Command, CommandResult, Context, Dispatcher, Event, Message, Timer};

const TIMER_STEP: u32 = 100;

pub struct Correlator {
    action_handlers: ActionHandlers,
    dispatcher_input_channel: mpsc::Sender<Command>,
    dispatcher_output_channel: mpsc::Receiver<CommandResult>,
    dispatcher_thread_handle: thread::JoinHandle<()>,
    exits_received: u32
}

impl Correlator {
    pub fn new(contexts: Vec<config::Context>, action_handlers: ActionHandlers) -> Correlator {
        let (dispatcher_input_channel, rx) = mpsc::channel();
        let (dispatcher_output_channel_tx, dispatcher_output_channel_rx) = mpsc::channel();
        let _ = Timer::from_chan(TIMER_STEP, dispatcher_input_channel.clone());

        let handle = thread::spawn(move || {
            let mut dispatcher = Dispatcher::new(contexts, dispatcher_output_channel_tx);
            dispatcher.start_loop(rx);
        });

        Correlator {
            action_handlers: action_handlers,
            dispatcher_input_channel: dispatcher_input_channel,
            dispatcher_output_channel: dispatcher_output_channel_rx,
            dispatcher_thread_handle: handle,
            exits_received: 0
        }
    }

    pub fn push_message(&mut self, message: Message) -> Result<(), mpsc::SendError<Command>> {
        self.consume_results();
        self.dispatcher_input_channel.send(Command::Dispatch(Event::Message(message)))
    }

    fn consume_results(&mut self) {
        for i in self.dispatcher_output_channel.try_recv() {
            if let CommandResult::Dispatch(result) = i {
                self.action_handlers.handle(result);
            }
        }
    }

    pub fn stop(mut self) -> thread::Result<()> {
        self.consume_results();
        self.stop_dispatcher();
        self.dispatcher_thread_handle.join()
    }

    fn stop_dispatcher(&mut self) {
        let _ = self.dispatcher_input_channel.send(Command::Exit);
        let _ = self.wait_for_dispatcher_to_exit();
    }

    fn wait_for_dispatcher_to_exit(&mut self) -> Result<(), ()> {
        loop {
            let value = self.dispatcher_output_channel.recv();
            match value {
                Ok(value) => {
                    try!(self.handle_command(value))
                },
                _ => {}
            }
        }
    }

    fn handle_command(&mut self, command: CommandResult) -> Result<(), ()> {
        match command {
            CommandResult::Dispatch(result) => self.action_handlers.handle(result),
            CommandResult::Exit => {
                if self.handle_exit_command() {
                    return Err(());
                }
            }
        }
        Ok(())
    }

    fn handle_exit_command(&mut self) -> bool {
        let _ = self.dispatcher_input_channel.send(Command::Exit);
        self.exits_received += 1;
        self.exits_received >= 1
    }
}
<|endoftext|>


<commit_before>extern crate "parser-combinators" as parser_combinators;
use self::parser_combinators::{between, spaces, many, many1, sep_by, alpha_num, satisfy, string,Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use super::ast;
use super::ast::ExprNode;

pub fn name<I>(input: State<I>) -> ParseResult<ast::NameNode, I>
    where I: Stream<Item=char> {
         let ident_start = satisfy(|c|
            c.is_alphabetic()
                || c == '='
                || c == '*'
                || c == '+'
                || c == '/'
                || c == '!'
                || c == '\\'
                || c == '?');
        let ident_body = satisfy(|c|
            c.is_alphanumeric()
                || c == '='
                || c == '*'
                || c == '+'
                || c == '/'
                || c == '!'
                || c == '\\'
                || c == '?');
        ident_start
            .and(many::<Vec<_>, _>(ident_body).map(|it|
                it.iter().fold(
                    String::new(),
                    |mut s: String, i| {
                        s.push(*i);
                        s
                    }
                    )
                ))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (ast::NameNode{ name: s}, x.1)
            })

}

pub fn expr<I>(input: State<I>) -> ParseResult<ast::ExprNode, I>
    where I: Stream<Item=char> {
        let mut sexpr = between(
            satisfy(|c| c == '('),
            satisfy(|c| c == ')'),
            (name as fn(_) -> _)
                .and(many(expr as fn(_) -> _))
                .map(|x| {
                    ast::ExprNode::SExpr(ast::SExprNode {
                        operator: x.0,
                        operands: (x.1)
                    })
                })
                );
        sexpr
            .or((name as fn(_) -> _).map(|x| ast::ExprNode::Name(x)))
            .parse_state(input)
}

#[cfg(test)]
mod tests {
    use ::ast;
    use super::expr;
    use super::parser_combinators::{Parser, ParseResult};

    #[test]
    fn test_basic_ident() {
        assert_eq!(
            (expr as fn (_) -> _).parse("ident").unwrap(),
            (ast::ExprNode::Name(ast::NameNode { name: "ident".to_string() }), "")
            );
    }

}
<commit_msg>Implemented parser for S-expressions<commit_after>extern crate "parser-combinators" as parser_combinators;
use self::parser_combinators::{between, spaces,space, many, many1, sep_by, alpha_num, satisfy, string,Parser, ParserExt, ParseResult};
use self::parser_combinators::primitives::{State, Stream};
use super::ast;
use super::ast::ExprNode;

pub fn name<I>(input: State<I>) -> ParseResult<ast::NameNode, I>
    where I: Stream<Item=char> {
         let ident_start = satisfy(|c|
            c.is_alphabetic()
                || c == '='
                || c == '*'
                || c == '+'
                || c == '/'
                || c == '!'
                || c == '\\'
                || c == '?');
        let ident_body = satisfy(|c|
            c.is_alphanumeric()
                || c == '='
                || c == '*'
                || c == '+'
                || c == '/'
                || c == '!'
                || c == '\\'
                || c == '?');
        ident_start
            .and(many::<Vec<_>, _>(ident_body).map(|it|
                it.iter().fold(
                    String::new(),
                    |mut s: String, i| {
                        s.push(*i);
                        s
                    }
                    )
                ))
            .parse_state(input)
            .map(|x| {
                let mut s = String::new();
                s.push((x.0).0);
                s.push_str(&(x.0).1);
                (ast::NameNode{ name: s}, x.1)
            })

}

pub fn expr<I>(input: State<I>) -> ParseResult<ast::ExprNode, I>
    where I: Stream<Item=char> {
        let spaces = spaces();
        let mut sexpr = between(
            satisfy(|c| c == '('),
            satisfy(|c| c == ')'),
            (name as fn(_) -> _)
                .and(many(expr as fn(_) -> _))
                .map(|x| {
                    ast::ExprNode::SExpr(ast::SExprNode {
                        operator: x.0,
                        operands: (x.1)
                    })
                })
                );
        spaces.clone().with(
            sexpr
                .or((name as fn(_) -> _).map(|x| ast::ExprNode::Name(x)))
            ).parse_state(input)
}

#[cfg(test)]
mod tests {
    use ::ast;
    use super::expr;
    use super::parser_combinators::{Parser, ParseResult};

    #[test]
    fn test_basic_ident() {
        assert_eq!(
            (expr as fn (_) -> _).parse("ident").unwrap(),
            (ast::ExprNode::Name(ast::NameNode { name: "ident".to_string() }), "")
            );
    }

        #[test]
    fn test_basic_sexpr() {
        assert_eq!(
            (expr as fn (_) -> _).parse("(ident arg1 arg2)").unwrap(),
            (ast::ExprNode::SExpr(ast::SExprNode {
                operator: ast::NameNode { name: "ident".to_string() },
                operands: vec![
                    ast::ExprNode::Name(ast::NameNode { name: "arg1".to_string() }),
                    ast::ExprNode::Name(ast::NameNode { name: "arg2".to_string() })
                ]
            }), "")
            );
    }

}
<|endoftext|>


<commit_before>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).

use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8], w: uint, h: uint|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display, DISPLAY_WIDTH, DISPLAY_HEIGHT);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<commit_msg>Remove w, h args from draw callback<commit_after>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).

use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8]|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<|endoftext|>


<commit_before>use crate::ctapi::MAP;
use crate::{http, Status, CONFIG};
use data_encoding::{BASE64, HEXLOWER};
use failure::Error;
use serde_json;
use std::slice;

#[allow(non_snake_case)]
#[derive(Deserialize, Debug)]
struct Response {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    #[serde(rename = "responseCode")]
    status: i8,
}

pub fn data(
    mut ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> Result<Status, Error> {
    if let Some(ctn_from_cfg) = CONFIG.read().ctn {
        debug!("Use ctn '{}' from configuration", ctn_from_cfg);
        ctn = ctn_from_cfg;
    }

    if !MAP.read().contains_key(&ctn) {
        error!("Card terminal has not been opened.");
        return Ok(Status::ERR_INVALID);
    }

    let pn = match MAP.read().get(&ctn) {
        None => return Err(format_err!("Failed to extract pn for given ctn!")),
        Some(pn) => *pn,
    };

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", HEXLOWER.encode(safe_command));

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    let json = json!({
        "dad": *safe_dad,
        "sad": *safe_sad,
        "lenc": lenc,
        "command": BASE64.encode(safe_command),
        "lenr": *safe_lenr
    });

    let path = format!("ct_data/{}/{}", ctn, pn);
    let response = http::request(&path, Some(json))?;

    match serde_json::from_str::<Response>(&response) {
        Err(why) => {
            debug!("{}", why);
            Err(format_err!("Unexpected server response found in body!"))
        }
        Ok(json) => {
            let status = Status::from_i8(json.status);
            if let Status::OK = status {
                let decoded = match BASE64.decode(&json.response.into_bytes()) {
                    Ok(content) => {
                        debug!("Decoded response field: {:?}", HEXLOWER.encode(&content));
                        content
                    }
                    Err(why) => {
                        debug!("{}", why);
                        return Err(format_err!("Failed to extract response."));
                    }
                };

                for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
                    *place = *element;
                }

                *safe_dad = json.dad;
                *safe_sad = json.sad;
                *safe_lenr = json.lenr;
            }
            Ok(status)
        }
    }
}

#[cfg(test)]
mod tests {

    use super::data;
    use crate::ctapi::MAP;
    use crate::{Settings, Status, CONFIG};
    use data_encoding::BASE64;
    use rand;
    use serde_json::{self, Value};
    use std::collections::HashMap;
    use std::env;
    use std::slice;
    use std::u16::MAX;
    use test_server::{self, HttpResponse};

    #[test]
    fn returns_err_invalid_if_terminal_closed() {
        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, _) = rand_params();

        assert_eq!(
            Some(Status::ERR_INVALID),
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).ok()
        );
    }

    #[test]
    fn returns_err_if_no_server() {
        env::set_var("K2_BASE_URL", "http://127.0.0.1:65432");
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn use_ctn_and_pn_in_request_path() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        let path = server.requests.next().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn post_body_contains_parameter() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (command, command_ptr, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) =
            rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(
            ctn,
            &mut dad,
            &mut sad,
            lenc,
            command_ptr,
            &mut lenr,
            response,
        );

        let body = server.requests.next().unwrap().body;
        let json: Value = serde_json::from_str(&body).unwrap();

        assert_eq!(*json.get("dad").unwrap(), json!(dad));
        assert_eq!(*json.get("sad").unwrap(), json!(sad));
        assert_eq!(
            *json.get("command").unwrap(),
            json!(BASE64.encode(&command))
        );
        assert_eq!(*json.get("lenc").unwrap(), json!(lenc));
        assert_eq!(*json.get("lenr").unwrap(), json!(lenr));

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn response_is_mapped_to_parameter() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":39,"sad":63,"lenr":2,"response":"kAA=","responseCode":0}"#)
        });
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        assert_eq!(dad, 39);
        assert_eq!(sad, 63);
        assert_eq!(2, lenr);

        let slice = unsafe { slice::from_raw_parts(response, lenr as usize) };
        assert_eq!([144, 0], slice);

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_err_if_server_response_is_not_200() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_err_if_server_response_not_contains_response_struct_as_json() {
        let server = test_server::new(0, |_| HttpResponse::Ok().body("hello world"));
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_response_status_from_valid_json_response_struct() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":1,"sad":1,"lenr":1,"response":"a=","responseCode":-11}"#)
        });
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert_eq!(
            Some(Status::ERR_MEMORY),
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).ok()
        );

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn use_ctn_and_pn_from_config() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        env::set_var("K2_CTN", format!("{}", ctn));
        env::set_var("K2_PN", format!("{}", pn));
        init_config_clear_map();

        MAP.write().insert(ctn, pn);

        let unused_ctn = rand::random::<u16>();

        let _ = data(unused_ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,);

        let path = server.requests.next().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));

        env::remove_var("K2_BASE_URL");
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
    }

    fn rand_params() -> (Vec<u8>, *const u8, u16, *mut u8, u16, u8, u8, u16, u16) {
        let mut command = vec![0; rand::random::<u16>() as usize];
        for x in command.iter_mut() {
            *x = rand::random::<u8>()
        }

        let command_ptr: *const u8 = command.as_ptr();
        let lenc: u16 = command.len() as u16;

        let mut response: [u8; MAX as usize] = [rand::random::<u8>(); MAX as usize];
        let response_ptr: *mut u8 = response.as_mut_ptr();
        let lenr: u16 = response.len() as u16;

        let dad = rand::random::<u8>();
        let sad = rand::random::<u8>();

        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        (
            command,
            command_ptr,
            lenc,
            response_ptr,
            lenr,
            dad,
            sad,
            ctn,
            pn,
        )
    }

    fn init_config_clear_map() {
        let mut config_guard = CONFIG.write();
        *config_guard = Settings::init().unwrap();
        drop(config_guard);

        let mut map_guard = MAP.write();
        *map_guard = HashMap::new();
        drop(map_guard);
    }
}
<commit_msg>cargo fmt<commit_after>use crate::ctapi::MAP;
use crate::{http, Status, CONFIG};
use data_encoding::{BASE64, HEXLOWER};
use failure::Error;
use serde_json;
use std::slice;

#[allow(non_snake_case)]
#[derive(Deserialize, Debug)]
struct Response {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    #[serde(rename = "responseCode")]
    status: i8,
}

pub fn data(
    mut ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> Result<Status, Error> {
    if let Some(ctn_from_cfg) = CONFIG.read().ctn {
        debug!("Use ctn '{}' from configuration", ctn_from_cfg);
        ctn = ctn_from_cfg;
    }

    if !MAP.read().contains_key(&ctn) {
        error!("Card terminal has not been opened.");
        return Ok(Status::ERR_INVALID);
    }

    let pn = match MAP.read().get(&ctn) {
        None => return Err(format_err!("Failed to extract pn for given ctn!")),
        Some(pn) => *pn,
    };

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", HEXLOWER.encode(safe_command));

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    let json = json!({
        "dad": *safe_dad,
        "sad": *safe_sad,
        "lenc": lenc,
        "command": BASE64.encode(safe_command),
        "lenr": *safe_lenr
    });

    let path = format!("ct_data/{}/{}", ctn, pn);
    let response = http::request(&path, Some(json))?;

    match serde_json::from_str::<Response>(&response) {
        Err(why) => {
            debug!("{}", why);
            Err(format_err!("Unexpected server response found in body!"))
        }
        Ok(json) => {
            let status = Status::from_i8(json.status);
            if let Status::OK = status {
                let decoded = match BASE64.decode(&json.response.into_bytes()) {
                    Ok(content) => {
                        debug!("Decoded response field: {:?}", HEXLOWER.encode(&content));
                        content
                    }
                    Err(why) => {
                        debug!("{}", why);
                        return Err(format_err!("Failed to extract response."));
                    }
                };

                for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
                    *place = *element;
                }

                *safe_dad = json.dad;
                *safe_sad = json.sad;
                *safe_lenr = json.lenr;
            }
            Ok(status)
        }
    }
}

#[cfg(test)]
mod tests {

    use super::data;
    use crate::ctapi::MAP;
    use crate::{Settings, Status, CONFIG};
    use data_encoding::BASE64;
    use rand;
    use serde_json::{self, Value};
    use std::collections::HashMap;
    use std::env;
    use std::slice;
    use std::u16::MAX;
    use test_server::{self, HttpResponse};

    #[test]
    fn returns_err_invalid_if_terminal_closed() {
        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, _) = rand_params();

        assert_eq!(
            Some(Status::ERR_INVALID),
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).ok()
        );
    }

    #[test]
    fn returns_err_if_no_server() {
        env::set_var("K2_BASE_URL", "http://127.0.0.1:65432");
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn use_ctn_and_pn_in_request_path() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        let path = server.requests.next().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn post_body_contains_parameter() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (command, command_ptr, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) =
            rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(
            ctn,
            &mut dad,
            &mut sad,
            lenc,
            command_ptr,
            &mut lenr,
            response,
        );

        let body = server.requests.next().unwrap().body;
        let json: Value = serde_json::from_str(&body).unwrap();

        assert_eq!(*json.get("dad").unwrap(), json!(dad));
        assert_eq!(*json.get("sad").unwrap(), json!(sad));
        assert_eq!(
            *json.get("command").unwrap(),
            json!(BASE64.encode(&command))
        );
        assert_eq!(*json.get("lenc").unwrap(), json!(lenc));
        assert_eq!(*json.get("lenr").unwrap(), json!(lenr));

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn response_is_mapped_to_parameter() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":39,"sad":63,"lenr":2,"response":"kAA=","responseCode":0}"#)
        });
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        let _ = data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        assert_eq!(dad, 39);
        assert_eq!(sad, 63);
        assert_eq!(2, lenr);

        let slice = unsafe { slice::from_raw_parts(response, lenr as usize) };
        assert_eq!([144, 0], slice);

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_err_if_server_response_is_not_200() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_err_if_server_response_not_contains_response_struct_as_json() {
        let server = test_server::new(0, |_| HttpResponse::Ok().body("hello world"));
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert!(data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn returns_response_status_from_valid_json_response_struct() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":1,"sad":1,"lenr":1,"response":"a=","responseCode":-11}"#)
        });
        env::set_var("K2_BASE_URL", server.url());
        init_config_clear_map();

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.write().insert(ctn, pn);

        assert_eq!(
            Some(Status::ERR_MEMORY),
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,).ok()
        );

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn use_ctn_and_pn_from_config() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (_, command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        env::set_var("K2_CTN", format!("{}", ctn));
        env::set_var("K2_PN", format!("{}", pn));
        init_config_clear_map();

        MAP.write().insert(ctn, pn);

        let unused_ctn = rand::random::<u16>();

        let _ = data(
            unused_ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response,
        );

        let path = server.requests.next().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));

        env::remove_var("K2_BASE_URL");
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
    }

    fn rand_params() -> (Vec<u8>, *const u8, u16, *mut u8, u16, u8, u8, u16, u16) {
        let mut command = vec![0; rand::random::<u16>() as usize];
        for x in command.iter_mut() {
            *x = rand::random::<u8>()
        }

        let command_ptr: *const u8 = command.as_ptr();
        let lenc: u16 = command.len() as u16;

        let mut response: [u8; MAX as usize] = [rand::random::<u8>(); MAX as usize];
        let response_ptr: *mut u8 = response.as_mut_ptr();
        let lenr: u16 = response.len() as u16;

        let dad = rand::random::<u8>();
        let sad = rand::random::<u8>();

        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        (
            command,
            command_ptr,
            lenc,
            response_ptr,
            lenr,
            dad,
            sad,
            ctn,
            pn,
        )
    }

    fn init_config_clear_map() {
        let mut config_guard = CONFIG.write();
        *config_guard = Settings::init().unwrap();
        drop(config_guard);

        let mut map_guard = MAP.write();
        *map_guard = HashMap::new();
        drop(map_guard);
    }
}
<|endoftext|>


<commit_before>use self::super::{MAP, StatusCode};
use self::super::super::http;

pub fn init(ctn: u16, pn: u16) -> StatusCode {
    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        error!("Card terminal has already been opened.");
        return StatusCode::ErrInvalid;
    }

    let path = format!("ct_init/{}/{}", ctn, pn);
    let response = http::request(&path, None);
    match response {
        Err(why) => {
            error!("{}", why);
            StatusCode::ErrHtsi
        }
        Ok(res) => {
            match res.status {
                200 => handle_ok_status(res.body, ctn, pn),
                _ => {
                    error!("Request failed! Server response was not OK!");
                    return StatusCode::ErrHtsi;
                }
            }
        }
    }
}

fn handle_ok_status(body: String, ctn: u16, pn: u16) -> StatusCode {
    match StatusCode::from_i8(body.parse::<i8>().unwrap()) {
        Ok(code) => {
            match code {
                StatusCode::Ok => {
                    // Store CTN
                    MAP.lock().unwrap().insert(ctn, pn);
                    debug!("Card terminal opened.");
                    code
                }
                _ => code,
            }
        }
        Err(why) => {
            error!("{}", why);
            StatusCode::ErrHtsi
        }
    }
}
<commit_msg>add ct_init tests<commit_after>use self::super::{MAP, StatusCode};
use self::super::super::http;

pub fn init(ctn: u16, pn: u16) -> StatusCode {
    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        error!("Card terminal has already been opened.");
        return StatusCode::ErrInvalid;
    }

    let path = format!("ct_init/{}/{}", ctn, pn);
    let response = http::request(&path, None);
    match response {
        Err(why) => {
            error!("{}", why);
            StatusCode::ErrHtsi
        }
        Ok(res) => {
            match res.status {
                200 => handle_ok_status(res.body, ctn, pn),
                _ => {
                    error!("Request failed! Server response was not OK!");
                    return StatusCode::ErrHtsi;
                }
            }
        }
    }
}

fn handle_ok_status(body: String, ctn: u16, pn: u16) -> StatusCode {
    match body.parse::<i8>() {
        Ok(status) => {
            match StatusCode::from_i8(status) {
                Ok(code) => {
                    match code {
                        StatusCode::Ok => {
                            // Store CTN
                            MAP.lock().unwrap().insert(ctn, pn);
                            debug!("Card terminal opened.");
                            code
                        }
                        _ => code,
                    }
                }
                _ => {
                    error!("Unexpected server reponse body!");
                    StatusCode::ErrHtsi
                }
            }
        }
        _ => {
            error!("Unexpected server reponse body!");
            StatusCode::ErrHtsi
        }
    }
}

#[cfg(test)]
mod tests {

    use super::init;
    use super::super::MAP;
    use mockito;
    use mockito::mock;
    use rand;
    use std::env;

    #[test]
    fn call_init_when_server_not_up_returns_minus_128() {
        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        env::set_var("K2_BASE_URL", mockito::SERVER_URL);

        assert_eq!(-128, init(ctn, pn))
    }

    #[test]
    fn call_init_on_open_terminal_returns_minus_1() {
        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        MAP.lock().unwrap().insert(ctn, pn);

        assert_eq!(-1, init(ctn, pn))
    }

    macro_rules! with_server_response {
        ($($name:ident: $value:expr,)*) => {
            $(
                #[test]
                fn $name() {
                    let (status, body, expected, map_contains_ctn) = $value;

                    let ctn = rand::random::<u16>();
                    let pn = rand::random::<u16>();

                    let path = format!("/ct_init/{}/{}", ctn, pn);
                    let _mock = mock("POST", path.as_str())
                        .with_status(status)
                        .with_body(body)
                        .create();

                    env::set_var("K2_BASE_URL", mockito::SERVER_URL);

                    assert_eq!(expected, init(ctn, pn));
                    assert_eq!(map_contains_ctn, MAP.lock().unwrap().contains_key(&ctn));

                    mockito::reset();
                }
            )*
        }
    }

    with_server_response! {
        not_found_returns_minus_128: (400, "Not found", -128, false),
        incorrect_response_returns_minus_128: (200, "hello world", -128, false),
        minus_1_response_returns_minus_1: (200, "-1", -1, false),
        zero_response_return_0: (200, "0", 0, true),
    }
}
<|endoftext|>


<commit_before>//! SSL support provided by OpenSSL.

extern crate openssl;

use std::io::net::ip::SocketAddr;
use std::io::net::tcp::TcpStream;
use std::io::{IoResult, IoError, ConnectionAborted, OtherIoError};
use self::openssl::ssl::{SslStream, SslContext, Sslv23};
use self::openssl::ssl::error::{SslError, StreamError, SslSessionClosed, OpenSslErrors};
use connecter::Connecter;

/// A TCP stream, either plain text or SSL.
///
/// This build was made with **OpenSSL** providing SSL support.
pub enum NetworkStream {
    NormalStream(TcpStream),
    SslProtectedStream(SslStream<TcpStream>),
}

impl Connecter for NetworkStream {
    fn connect(addr: SocketAddr, _host: &str, use_ssl: bool) -> IoResult<NetworkStream> {
        let stream = try!(TcpStream::connect(format!("{}", addr.ip).as_slice(), addr.port));
        if use_ssl {
            let context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error));
            let ssl_stream = try!(SslStream::new(&context, stream).map_err(lift_ssl_error));
            Ok(SslProtectedStream(ssl_stream))
        } else {
            Ok(NormalStream(stream))
        }
    }
}

impl Reader for NetworkStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match *self {
            NormalStream(ref mut ns) => ns.read(buf),
            SslProtectedStream(ref mut ns) => ns.read(buf),
        }
    }
}

impl Writer for NetworkStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match *self {
            NormalStream(ref mut ns) => ns.write(buf),
            SslProtectedStream(ref mut ns) => ns.write(buf),
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match *self {
            NormalStream(ref mut ns) => ns.flush(),
            SslProtectedStream(ref mut ns) => ns.flush(),
        }
    }
}

fn lift_ssl_error(ssl: SslError) -> IoError {
    match ssl {
        StreamError(err) => err,
        SslSessionClosed => IoError {
            kind: ConnectionAborted,
            desc: "SSL Connection Closed",
            detail: None
        },
        // Unfortunately throw this away. No way to support this
        // detail without a better Error abstraction.
        OpenSslErrors(errs) => IoError {
            kind: OtherIoError,
            desc: "Error in OpenSSL",
            detail: Some(format!("{}", errs))
        }
    }
}

<commit_msg>Fix OpenSSL error when using HTTPS<commit_after>//! SSL support provided by OpenSSL.

extern crate openssl;

use std::io::net::ip::SocketAddr;
use std::io::net::tcp::TcpStream;
use std::io::{IoResult, IoError, ConnectionAborted, OtherIoError};
use self::openssl::ssl::{SslStream, SslContext, Sslv23, Ssl};
use self::openssl::ssl::error::{SslError, StreamError, SslSessionClosed, OpenSslErrors};
use connecter::Connecter;

/// A TCP stream, either plain text or SSL.
///
/// This build was made with **OpenSSL** providing SSL support.
pub enum NetworkStream {
    NormalStream(TcpStream),
    SslProtectedStream(SslStream<TcpStream>),
}

impl Connecter for NetworkStream {
    fn connect(addr: SocketAddr, _host: &str, use_ssl: bool) -> IoResult<NetworkStream> {
        let stream = try!(TcpStream::connect(format!("{}", addr.ip).as_slice(), addr.port));
        if use_ssl {
            let context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error));
            let ssl = try!(Ssl::new(&context).map_err(lift_ssl_error));
            ssl.set_hostname(_host);
            let ssl_stream = try!(SslStream::new_from(ssl, stream).map_err(lift_ssl_error));
            Ok(SslProtectedStream(ssl_stream))
        } else {
            Ok(NormalStream(stream))
        }
    }
}

impl Reader for NetworkStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match *self {
            NormalStream(ref mut ns) => ns.read(buf),
            SslProtectedStream(ref mut ns) => ns.read(buf),
        }
    }
}

impl Writer for NetworkStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match *self {
            NormalStream(ref mut ns) => ns.write(buf),
            SslProtectedStream(ref mut ns) => ns.write(buf),
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match *self {
            NormalStream(ref mut ns) => ns.flush(),
            SslProtectedStream(ref mut ns) => ns.flush(),
        }
    }
}

fn lift_ssl_error(ssl: SslError) -> IoError {
    match ssl {
        StreamError(err) => err,
        SslSessionClosed => IoError {
            kind: ConnectionAborted,
            desc: "SSL Connection Closed",
            detail: None
        },
        // Unfortunately throw this away. No way to support this
        // detail without a better Error abstraction.
        OpenSslErrors(errs) => IoError {
            kind: OtherIoError,
            desc: "Error in OpenSSL",
            detail: Some(format!("{}", errs))
        }
    }
}
<|endoftext|>


<commit_before>pub struct Handler;

impl Handler {
    pub unsafe fn new() -> Handler {
        Handler
    }
}

pub unsafe fn init() {}

pub unsafe fn cleanup() {}
<commit_msg>Remove stack overflow handler stub for wasm.<commit_after>pub unsafe fn init() {}

pub unsafe fn cleanup() {}
<|endoftext|>


<commit_before>extern crate core;

use core::num::FromPrimitive;
use core::fmt;

use std::rand::random;

use Direction::{SpinUp, SpinDown, SpinSuper};
use Plan::{Trivial, OddBall};

#[derive(Copy)]
#[derive(Show)]
#[derive(PartialEq)]
enum Direction {
    SpinUp,
    SpinDown,
    SpinSuper,
}

impl fmt::String for Direction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r = match self {
            &SpinUp => "SpinUp",
            &SpinDown => "SpinDown",
            &SpinSuper => "SpinSuper"
        };
        write!(f, "{}", r)
    }
}

impl fmt::String for Detector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Detector::{D12, D3, D9};
        let r = match self {
            &D12 => "12 oclock",
            &D3 => "3 oclock",
            &D9 => "9 oclock"
        };
        write!(f, "{}", r)
    }
}

struct Pair<T> {
    lhs: T,
    rhs: T
}

struct Particle {
    spin: Direction
}

enum Plan {
    Trivial, // up-up-up -> down-down-down
    OddBall  // up-down-up -> down-up-down
}

#[derive(Show)]
#[derive(PartialEq)]
enum Detector {
    D12, // 12 o'clock
    D3, // 3 o'clock
    D9, // 9 o' clock
}

impl Particle {
    fn new_pair () -> Pair<Particle> {
        let d1 = SpinSuper;
        let d2 = SpinSuper;
        let p1 = Particle{spin: d1};
        let p2 = Particle{spin: d2};

        return Pair{lhs: p1, rhs: p2};
    }

    pub fn measure (&mut self, detector: &Detector) {
        self.spin = match detector {
            &Detector::D12 => SpinUp,
            &Detector::D3 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
            &Detector::D9 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
        };
    }

    // NOTE on spooky and premeditated only if measured in the same
    // direction must the spins be opposite

    // measure with with a message
    pub fn spooky (&mut self, friend: &mut Particle) -> Pair<Direction> {

        let detector1 = Detector::D12;
        let detector2 = Particle::get_detector_direction();

        self.measure (&detector1);

        if detector1 == detector2 { // 1/3
            friend.spin = match self.spin {
                SpinUp => SpinDown,
                SpinDown => SpinUp,
                _ => unreachable!()
            };
        } else { // 2/3
            friend.measure(&detector2);
        }

        return Pair{lhs: self.spin, rhs: friend.spin};
    }

    fn get_detector_direction () -> Detector {
        let rnd = random::<f32>();

        let detector = match rnd {
            0.0       ... 0.3333333 => Detector::D12,
            0.3333333 ... 0.6666666 => Detector::D3,
            0.6666666 ... 1.00 => Detector::D9,
            _ => unreachable!()
        };

        return detector;
    }

    pub fn hidden_information (&mut self, friend: &mut Particle, plan: Plan) -> Pair<Direction> {

        let rnd = random::<f32>();

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinUp,
            OddBall => match detector {
                Detector::D3 => SpinDown,
                _ => SpinUp
            }
        };
        friend.spin = spin;

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinDown,
            OddBall => match detector {
                Detector::D3 => SpinUp,
                _ => SpinDown
            }
        };
        self.spin = spin;

        return Pair{lhs: self.spin, rhs: friend.spin};
    }
}

/**
 * run spooky for trials number of trials
 */
fn run_spooky (trials: f64) {
    let mut num_different: f64 = 0f64;

    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        lhs.spooky(&mut rhs);

        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for spooky {}%", 100f64 * (num_different) / trials as f64);
    println!("      Should be about 1/2 or 50%");
}

/**
 * Run test for hidden information, set the plan_probability to choose
 * OddBall vs Trivial
 */
fn run_hidden(trials: f64, plan_probability: f64) {

    let mut num_different = 0f64;
    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        if random::<f32>() < 0.50 {
            lhs.hidden_information(&mut rhs, OddBall);
        } else {
            lhs.hidden_information(&mut rhs, Trivial);
        }

        //println!("lhs.spin {}, rhs.spin {}", lhs.spin, rhs.spin);
        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for hidden info {}%", 100f64 * (num_different) / trials as f64);
    println!("  Should be greater than 5/9th or {}%", 100.0 * 5.0/9.0);

}

// hidden_information should give +55.6% difference, spooky would give 50%
fn main () {

    let mut trials: f64 = 1000000f64;

    run_spooky(trials);
    run_hidden(trials, 0.5);
}
<commit_msg>run for different percentage of plans<commit_after>extern crate core;

use core::num::FromPrimitive;
use core::fmt;

use std::rand::random;

use Direction::{SpinUp, SpinDown, SpinSuper};
use Plan::{Trivial, OddBall};

#[derive(Copy)]
#[derive(Show)]
#[derive(PartialEq)]
enum Direction {
    SpinUp,
    SpinDown,
    SpinSuper,
}

impl fmt::String for Direction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r = match self {
            &SpinUp => "SpinUp",
            &SpinDown => "SpinDown",
            &SpinSuper => "SpinSuper"
        };
        write!(f, "{}", r)
    }
}

impl fmt::String for Detector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Detector::{D12, D3, D9};
        let r = match self {
            &D12 => "12 oclock",
            &D3 => "3 oclock",
            &D9 => "9 oclock"
        };
        write!(f, "{}", r)
    }
}

struct Pair<T> {
    lhs: T,
    rhs: T
}

struct Particle {
    spin: Direction
}

enum Plan {
    Trivial, // up-up-up -> down-down-down
    OddBall  // up-down-up -> down-up-down
}

#[derive(Show)]
#[derive(PartialEq)]
enum Detector {
    D12, // 12 o'clock
    D3, // 3 o'clock
    D9, // 9 o' clock
}

impl Particle {
    fn new_pair () -> Pair<Particle> {
        let d1 = SpinSuper;
        let d2 = SpinSuper;
        let p1 = Particle{spin: d1};
        let p2 = Particle{spin: d2};

        return Pair{lhs: p1, rhs: p2};
    }

    pub fn measure (&mut self, detector: &Detector) {
        self.spin = match detector {
            &Detector::D12 => SpinUp,
            &Detector::D3 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
            &Detector::D9 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
        };
    }

    // NOTE on spooky and premeditated only if measured in the same
    // direction must the spins be opposite

    // measure with with a message
    pub fn spooky (&mut self, friend: &mut Particle) -> Pair<Direction> {

        let detector1 = Detector::D12;
        let detector2 = Particle::get_detector_direction();

        self.measure (&detector1);

        if detector1 == detector2 { // 1/3
            friend.spin = match self.spin {
                SpinUp => SpinDown,
                SpinDown => SpinUp,
                _ => unreachable!()
            };
        } else { // 2/3
            friend.measure(&detector2);
        }

        return Pair{lhs: self.spin, rhs: friend.spin};
    }

    fn get_detector_direction () -> Detector {
        let rnd = random::<f32>();

        let detector = match rnd {
            0.0       ... 0.3333333 => Detector::D12,
            0.3333333 ... 0.6666666 => Detector::D3,
            0.6666666 ... 1.00 => Detector::D9,
            _ => unreachable!()
        };

        return detector;
    }

    pub fn hidden_information (&mut self, friend: &mut Particle, plan: Plan) -> Pair<Direction> {

        let rnd = random::<f32>();

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinUp,
            OddBall => match detector {
                Detector::D3 => SpinDown,
                _ => SpinUp
            }
        };
        friend.spin = spin;

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinDown,
            OddBall => match detector {
                Detector::D3 => SpinUp,
                _ => SpinDown
            }
        };
        self.spin = spin;

        return Pair{lhs: self.spin, rhs: friend.spin};
    }
}

/**
 * run spooky for trials number of trials
 */
fn run_spooky (trials: f64) {
    let mut num_different: f64 = 0f64;

    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        lhs.spooky(&mut rhs);

        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for spooky {}%", 100f64 * (num_different) / trials as f64);
    println!("      Should be about 1/2 or 50%");
}

/**
 * Run test for hidden information, set the plan_probability to choose
 * OddBall vs Trivial
 */
fn run_hidden(trials: f64, plan_probability: f32) {

    let mut num_different = 0f64;
    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        if random::<f32>() < plan_probability {
            lhs.hidden_information(&mut rhs, OddBall);
        } else {
            lhs.hidden_information(&mut rhs, Trivial);
        }

        //println!("lhs.spin {}, rhs.spin {}", lhs.spin, rhs.spin);
        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for hidden info {}%", 100f64 * (num_different) / trials as f64);
    println!("  Should be greater than 5/9th or {}%", 100.0 * 5.0/9.0);
    println!("With OddBall choosen {}% of the time and Trivial {}% of the time",
             100.0 * plan_probability,
             100.0 * (1.0 - plan_probability));

}

// hidden_information should give +55.6% difference, spooky would give 50%
fn main () {

    let mut trials: f64 = 1000000f64;

    run_spooky(trials);
    run_hidden(trials, 0.5);
    run_hidden(trials, 1.0);
    run_hidden(trials, 0.0);
}
<|endoftext|>


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

use crate::core::core::{self, amount_to_hr_string};
use crate::core::global;
use crate::libwallet::types::{AcctPathMapping, OutputData, OutputStatus, TxLogEntry, WalletInfo};
use crate::libwallet::Error;
use crate::util;
use crate::util::secp::pedersen;
use prettytable;
use std::io::prelude::Write;
use term;

/// Display outputs in a pretty way
pub fn outputs(
	account: &str,
	cur_height: u64,
	validated: bool,
	outputs: Vec<(OutputData, pedersen::Commitment)>,
	dark_background_color_scheme: bool,
) -> Result<(), Error> {
	let title = format!(
		"Wallet Outputs - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Output Commitment",
		bMG->"MMR Index",
		bMG->"Block Height",
		bMG->"Locked Until",
		bMG->"Status",
		bMG->"Coinbase?",
		bMG->"# Confirms",
		bMG->"Value",
		bMG->"Tx"
	]);

	for (out, commit) in outputs {
		let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
		let index = match out.mmr_index {
			None => "None".to_owned(),
			Some(t) => t.to_string(),
		};
		let height = format!("{}", out.height);
		let lock_height = format!("{}", out.lock_height);
		let is_coinbase = format!("{}", out.is_coinbase);

		// Mark unconfirmed coinbase outputs as "Mining" instead of "Unconfirmed"
		let status = match out.status {
			OutputStatus::Unconfirmed if out.is_coinbase => "Mining".to_string(),
			_ => format!("{}", out.status),
		};

		let num_confirmations = format!("{}", out.num_confirmations(cur_height));
		let value = format!("{}", core::amount_to_hr_string(out.value, false));
		let tx = match out.tx_log_entry {
			None => "".to_owned(),
			Some(t) => t.to_string(),
		};

		if dark_background_color_scheme {
			table.add_row(row![
				bFC->commit,
				bFB->index,
				bFB->height,
				bFB->lock_height,
				bFR->status,
				bFY->is_coinbase,
				bFB->num_confirmations,
				bFG->value,
				bFC->tx,
			]);
		} else {
			table.add_row(row![
				bFD->commit,
				bFB->height,
				bFB->lock_height,
				bFR->status,
				bFD->is_coinbase,
				bFB->num_confirmations,
				bFG->value,
				bFD->tx,
			]);
		}
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}

/// Display transaction log in a pretty way
pub fn txs(
	account: &str,
	cur_height: u64,
	validated: bool,
	txs: Vec<TxLogEntry>,
	include_status: bool,
	dark_background_color_scheme: bool,
) -> Result<(), Error> {
	let title = format!(
		"Transaction Log - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Id",
		bMG->"Type",
		bMG->"Shared Transaction Id",
		bMG->"Creation Time",
		bMG->"Confirmed?",
		bMG->"Confirmation Time",
		bMG->"Num. \nInputs",
		bMG->"Num. \nOutputs",
		bMG->"Amount \nCredited",
		bMG->"Amount \nDebited",
		bMG->"Fee",
		bMG->"Net \nDifference",
		bMG->"Tx \nData",
	]);

	for t in txs {
		let id = format!("{}", t.id);
		let slate_id = match t.tx_slate_id {
			Some(m) => format!("{}", m),
			None => "None".to_owned(),
		};
		let entry_type = format!("{}", t.tx_type);
		let creation_ts = format!("{}", t.creation_ts.format("%Y-%m-%d %H:%M:%S"));
		let confirmation_ts = match t.confirmation_ts {
			Some(m) => format!("{}", m.format("%Y-%m-%d %H:%M:%S")),
			None => "None".to_owned(),
		};
		let confirmed = format!("{}", t.confirmed);
		let num_inputs = format!("{}", t.num_inputs);
		let num_outputs = format!("{}", t.num_outputs);
		let amount_debited_str = core::amount_to_hr_string(t.amount_debited, true);
		let amount_credited_str = core::amount_to_hr_string(t.amount_credited, true);
		let fee = match t.fee {
			Some(f) => format!("{}", core::amount_to_hr_string(f, true)),
			None => "None".to_owned(),
		};
		let net_diff = if t.amount_credited >= t.amount_debited {
			core::amount_to_hr_string(t.amount_credited - t.amount_debited, true)
		} else {
			format!(
				"-{}",
				core::amount_to_hr_string(t.amount_debited - t.amount_credited, true)
			)
		};
		let tx_data = match t.stored_tx {
			Some(t) => format!("{}", t),
			None => "None".to_owned(),
		};
		if dark_background_color_scheme {
			table.add_row(row![
				bFC->id,
				bFC->entry_type,
				bFC->slate_id,
				bFB->creation_ts,
				bFC->confirmed,
				bFB->confirmation_ts,
				bFC->num_inputs,
				bFC->num_outputs,
				bFG->amount_credited_str,
				bFR->amount_debited_str,
				bFR->fee,
				bFY->net_diff,
				bFb->tx_data,
			]);
		} else {
			if t.confirmed {
				table.add_row(row![
					bFD->id,
					bFb->entry_type,
					bFD->slate_id,
					bFB->creation_ts,
					bFg->confirmed,
					bFB->confirmation_ts,
					bFD->num_inputs,
					bFD->num_outputs,
					bFG->amount_credited_str,
					bFD->amount_debited_str,
					bFD->fee,
					bFG->net_diff,
					bFB->tx_data,
				]);
			} else {
				table.add_row(row![
					bFD->id,
					bFb->entry_type,
					bFD->slate_id,
					bFB->creation_ts,
					bFR->confirmed,
					bFB->confirmation_ts,
					bFD->num_inputs,
					bFD->num_outputs,
					bFG->amount_credited_str,
					bFD->amount_debited_str,
					bFD->fee,
					bFG->net_diff,
					bFB->tx_data,
				]);
			}
		}
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated && include_status {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}
/// Display summary info in a pretty way
pub fn info(
	account: &str,
	wallet_info: &WalletInfo,
	validated: bool,
	dark_background_color_scheme: bool,
) {
	println!(
		"\n____ Wallet Summary Info - Account '{}' as of height {} ____\n",
		account, wallet_info.last_confirmed_height,
	);

	let mut table = table!();

	if dark_background_color_scheme {
		table.add_row(row![
			bFG->"Total",
			FG->amount_to_hr_string(wallet_info.total, false)
		]);
		// Only dispay "Immature Coinbase" if we have related outputs in the wallet.
		// This row just introduces confusion if the wallet does not receive coinbase rewards.
		if wallet_info.amount_immature > 0 {
			table.add_row(row![
				bFY->format!("Immature Coinbase (< {})", global::coinbase_maturity()),
				FY->amount_to_hr_string(wallet_info.amount_immature, false)
			]);
		}
		table.add_row(row![
			bFY->format!("Awaiting Confirmation (< {})", wallet_info.minimum_confirmations),
			FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)
		]);
		table.add_row(row![
			Fr->"Locked by previous transaction",
			Fr->amount_to_hr_string(wallet_info.amount_locked, false)
		]);
		table.add_row(row![
			Fw->"--------------------------------",
			Fw->"-------------"
		]);
		table.add_row(row![
			bFG->"Currently Spendable",
			FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)
		]);
	} else {
		table.add_row(row![
			bFG->"Total",
			FG->amount_to_hr_string(wallet_info.total, false)
		]);
		// Only dispay "Immature Coinbase" if we have related outputs in the wallet.
		// This row just introduces confusion if the wallet does not receive coinbase rewards.
		if wallet_info.amount_immature > 0 {
			table.add_row(row![
				bFB->format!("Immature Coinbase (< {})", global::coinbase_maturity()),
				FB->amount_to_hr_string(wallet_info.amount_immature, false)
			]);
		}
		table.add_row(row![
			bFB->format!("Awaiting Confirmation (< {})", wallet_info.minimum_confirmations),
			FB->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)
		]);
		table.add_row(row![
			Fr->"Locked by previous transaction",
			Fr->amount_to_hr_string(wallet_info.amount_locked, false)
		]);
		table.add_row(row![
			Fw->"--------------------------------",
			Fw->"-------------"
		]);
		table.add_row(row![
			bFG->"Currently Spendable",
			FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)
		]);
	};
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data against a live chain. \
			 The above is from local cache and only valid up to the given height! \
			 (is your `grin server` offline or broken?)"
		);
	}
}
/// Display list of wallet accounts in a pretty way
pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
	println!("\n____ Wallet Accounts ____\n",);
	let mut table = table!();

	table.set_titles(row![
		mMG->"Name",
		bMG->"Parent BIP-32 Derivation Path",
	]);
	for m in acct_mappings {
		table.add_row(row![
			bFC->m.label,
			bGC->m.path.to_bip_32_string(),
		]);
	}
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
}
<commit_msg>wallet: avoid display the long file name on txs (#2445)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::core::core::{self, amount_to_hr_string};
use crate::core::global;
use crate::libwallet::types::{AcctPathMapping, OutputData, OutputStatus, TxLogEntry, WalletInfo};
use crate::libwallet::Error;
use crate::util;
use crate::util::secp::pedersen;
use prettytable;
use std::io::prelude::Write;
use term;

/// Display outputs in a pretty way
pub fn outputs(
	account: &str,
	cur_height: u64,
	validated: bool,
	outputs: Vec<(OutputData, pedersen::Commitment)>,
	dark_background_color_scheme: bool,
) -> Result<(), Error> {
	let title = format!(
		"Wallet Outputs - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Output Commitment",
		bMG->"MMR Index",
		bMG->"Block Height",
		bMG->"Locked Until",
		bMG->"Status",
		bMG->"Coinbase?",
		bMG->"# Confirms",
		bMG->"Value",
		bMG->"Tx"
	]);

	for (out, commit) in outputs {
		let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
		let index = match out.mmr_index {
			None => "None".to_owned(),
			Some(t) => t.to_string(),
		};
		let height = format!("{}", out.height);
		let lock_height = format!("{}", out.lock_height);
		let is_coinbase = format!("{}", out.is_coinbase);

		// Mark unconfirmed coinbase outputs as "Mining" instead of "Unconfirmed"
		let status = match out.status {
			OutputStatus::Unconfirmed if out.is_coinbase => "Mining".to_string(),
			_ => format!("{}", out.status),
		};

		let num_confirmations = format!("{}", out.num_confirmations(cur_height));
		let value = format!("{}", core::amount_to_hr_string(out.value, false));
		let tx = match out.tx_log_entry {
			None => "".to_owned(),
			Some(t) => t.to_string(),
		};

		if dark_background_color_scheme {
			table.add_row(row![
				bFC->commit,
				bFB->index,
				bFB->height,
				bFB->lock_height,
				bFR->status,
				bFY->is_coinbase,
				bFB->num_confirmations,
				bFG->value,
				bFC->tx,
			]);
		} else {
			table.add_row(row![
				bFD->commit,
				bFB->height,
				bFB->lock_height,
				bFR->status,
				bFD->is_coinbase,
				bFB->num_confirmations,
				bFG->value,
				bFD->tx,
			]);
		}
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}

/// Display transaction log in a pretty way
pub fn txs(
	account: &str,
	cur_height: u64,
	validated: bool,
	txs: Vec<TxLogEntry>,
	include_status: bool,
	dark_background_color_scheme: bool,
) -> Result<(), Error> {
	let title = format!(
		"Transaction Log - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Id",
		bMG->"Type",
		bMG->"Shared Transaction Id",
		bMG->"Creation Time",
		bMG->"Confirmed?",
		bMG->"Confirmation Time",
		bMG->"Num. \nInputs",
		bMG->"Num. \nOutputs",
		bMG->"Amount \nCredited",
		bMG->"Amount \nDebited",
		bMG->"Fee",
		bMG->"Net \nDifference",
		bMG->"Tx \nData",
	]);

	for t in txs {
		let id = format!("{}", t.id);
		let slate_id = match t.tx_slate_id {
			Some(m) => format!("{}", m),
			None => "None".to_owned(),
		};
		let entry_type = format!("{}", t.tx_type);
		let creation_ts = format!("{}", t.creation_ts.format("%Y-%m-%d %H:%M:%S"));
		let confirmation_ts = match t.confirmation_ts {
			Some(m) => format!("{}", m.format("%Y-%m-%d %H:%M:%S")),
			None => "None".to_owned(),
		};
		let confirmed = format!("{}", t.confirmed);
		let num_inputs = format!("{}", t.num_inputs);
		let num_outputs = format!("{}", t.num_outputs);
		let amount_debited_str = core::amount_to_hr_string(t.amount_debited, true);
		let amount_credited_str = core::amount_to_hr_string(t.amount_credited, true);
		let fee = match t.fee {
			Some(f) => format!("{}", core::amount_to_hr_string(f, true)),
			None => "None".to_owned(),
		};
		let net_diff = if t.amount_credited >= t.amount_debited {
			core::amount_to_hr_string(t.amount_credited - t.amount_debited, true)
		} else {
			format!(
				"-{}",
				core::amount_to_hr_string(t.amount_debited - t.amount_credited, true)
			)
		};
		let tx_data = match t.stored_tx {
			Some(_) => "Yes".to_owned(),
			None => "None".to_owned(),
		};
		if dark_background_color_scheme {
			table.add_row(row![
				bFC->id,
				bFC->entry_type,
				bFC->slate_id,
				bFB->creation_ts,
				bFC->confirmed,
				bFB->confirmation_ts,
				bFC->num_inputs,
				bFC->num_outputs,
				bFG->amount_credited_str,
				bFR->amount_debited_str,
				bFR->fee,
				bFY->net_diff,
				bFb->tx_data,
			]);
		} else {
			if t.confirmed {
				table.add_row(row![
					bFD->id,
					bFb->entry_type,
					bFD->slate_id,
					bFB->creation_ts,
					bFg->confirmed,
					bFB->confirmation_ts,
					bFD->num_inputs,
					bFD->num_outputs,
					bFG->amount_credited_str,
					bFD->amount_debited_str,
					bFD->fee,
					bFG->net_diff,
					bFB->tx_data,
				]);
			} else {
				table.add_row(row![
					bFD->id,
					bFb->entry_type,
					bFD->slate_id,
					bFB->creation_ts,
					bFR->confirmed,
					bFB->confirmation_ts,
					bFD->num_inputs,
					bFD->num_outputs,
					bFG->amount_credited_str,
					bFD->amount_debited_str,
					bFD->fee,
					bFG->net_diff,
					bFB->tx_data,
				]);
			}
		}
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated && include_status {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}
/// Display summary info in a pretty way
pub fn info(
	account: &str,
	wallet_info: &WalletInfo,
	validated: bool,
	dark_background_color_scheme: bool,
) {
	println!(
		"\n____ Wallet Summary Info - Account '{}' as of height {} ____\n",
		account, wallet_info.last_confirmed_height,
	);

	let mut table = table!();

	if dark_background_color_scheme {
		table.add_row(row![
			bFG->"Total",
			FG->amount_to_hr_string(wallet_info.total, false)
		]);
		// Only dispay "Immature Coinbase" if we have related outputs in the wallet.
		// This row just introduces confusion if the wallet does not receive coinbase rewards.
		if wallet_info.amount_immature > 0 {
			table.add_row(row![
				bFY->format!("Immature Coinbase (< {})", global::coinbase_maturity()),
				FY->amount_to_hr_string(wallet_info.amount_immature, false)
			]);
		}
		table.add_row(row![
			bFY->format!("Awaiting Confirmation (< {})", wallet_info.minimum_confirmations),
			FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)
		]);
		table.add_row(row![
			Fr->"Locked by previous transaction",
			Fr->amount_to_hr_string(wallet_info.amount_locked, false)
		]);
		table.add_row(row![
			Fw->"--------------------------------",
			Fw->"-------------"
		]);
		table.add_row(row![
			bFG->"Currently Spendable",
			FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)
		]);
	} else {
		table.add_row(row![
			bFG->"Total",
			FG->amount_to_hr_string(wallet_info.total, false)
		]);
		// Only dispay "Immature Coinbase" if we have related outputs in the wallet.
		// This row just introduces confusion if the wallet does not receive coinbase rewards.
		if wallet_info.amount_immature > 0 {
			table.add_row(row![
				bFB->format!("Immature Coinbase (< {})", global::coinbase_maturity()),
				FB->amount_to_hr_string(wallet_info.amount_immature, false)
			]);
		}
		table.add_row(row![
			bFB->format!("Awaiting Confirmation (< {})", wallet_info.minimum_confirmations),
			FB->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)
		]);
		table.add_row(row![
			Fr->"Locked by previous transaction",
			Fr->amount_to_hr_string(wallet_info.amount_locked, false)
		]);
		table.add_row(row![
			Fw->"--------------------------------",
			Fw->"-------------"
		]);
		table.add_row(row![
			bFG->"Currently Spendable",
			FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)
		]);
	};
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data against a live chain. \
			 The above is from local cache and only valid up to the given height! \
			 (is your `grin server` offline or broken?)"
		);
	}
}
/// Display list of wallet accounts in a pretty way
pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
	println!("\n____ Wallet Accounts ____\n",);
	let mut table = table!();

	table.set_titles(row![
		mMG->"Name",
		bMG->"Parent BIP-32 Derivation Path",
	]);
	for m in acct_mappings {
		table.add_row(row![
			bFC->m.label,
			bGC->m.path.to_bip_32_string(),
		]);
	}
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
}
<|endoftext|>