<commit_before>/// Still to test:
///
/// * compression
/// * secure connections

use super::*;
use std::collections::HashSet;
use std::time::Duration;
use kafka::client::{KafkaClient, PartitionOffset, FetchPartition, ProduceMessage, RequiredAcks};
use kafka::client::fetch::Response;

fn flatten_fetched_messages(resps: &Vec<Response>) -> Vec<(&str, i32, &[u8])> {
    let mut messages = Vec::new();

    for resp in resps {
        for topic in resp.topics() {
            for partition in topic.partitions() {
                for msg in partition.data().as_ref().unwrap().messages() {
                    messages.push((topic.topic(), partition.partition(), msg.value));
                }
            }
        }
    }

    messages
}

#[test]
fn test_kafka_client_load_metadata() {
    let hosts = vec![LOCAL_KAFKA_BOOTSTRAP_HOST.to_owned()];
    let client_id = "test-id".to_string();
    let mut client = KafkaClient::new(hosts.clone());
    client.set_client_id(client_id.clone());
    client.load_metadata_all().unwrap();

    let topics = client.topics();

    // sanity checks
    assert_eq!(hosts.as_ref() as &[String], client.hosts());
    assert_eq!(&client_id, client.client_id());

    // names
    let topic_names: HashSet<&str> = topics.names()
        // don't count the consumer offsets internal topic
        .filter(|name| *name != KAFKA_CONSUMER_OFFSETS_TOPIC_NAME)
        .collect();
    let mut correct_topic_names: HashSet<&str> = HashSet::new();
    correct_topic_names.insert(TEST_TOPIC_NAME);
    correct_topic_names.insert(TEST_TOPIC_NAME_2);

    assert_eq!(correct_topic_names, topic_names);

    // partitions
    let mut topic_partitions = topics.partitions(TEST_TOPIC_NAME).unwrap().available_ids();
    let mut correct_topic_partitions = TEST_TOPIC_PARTITIONS.to_vec();
    assert_eq!(correct_topic_partitions, topic_partitions);

    topic_partitions = topics
        .partitions(TEST_TOPIC_NAME_2)
        .unwrap()
        .available_ids();
    correct_topic_partitions = TEST_TOPIC_PARTITIONS.to_vec();
    assert_eq!(correct_topic_partitions, topic_partitions);
}

#[test]
fn test_produce_fetch_messages() {
    let mut client = new_ready_kafka_client();

    // first send the messages and verify correct confirmation responses
    // from kafka
    let req = vec![
        ProduceMessage::new(TEST_TOPIC_NAME, 0, None, Some("a".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME, 1, None, Some("b".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME_2, 0, None, Some("c".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME_2, 1, None, Some("d".as_bytes())),
    ];

    let resp = client
        .produce_messages(RequiredAcks::All, Duration::from_millis(1000), req)
        .unwrap();

    assert_eq!(2, resp.len());

    // need to keep track of the offsets so we can fetch them next
    let mut fetches = Vec::new();

    for confirm in &resp {
        assert!(confirm.topic == TEST_TOPIC_NAME || confirm.topic == TEST_TOPIC_NAME_2);
        assert_eq!(2, confirm.partition_confirms.len());

        assert!(confirm.partition_confirms.iter().any(|part_confirm| {
            part_confirm.partition == 0 && part_confirm.offset.is_ok()
        }));

        assert!(confirm.partition_confirms.iter().any(|part_confirm| {
            part_confirm.partition == 1 && part_confirm.offset.is_ok()
        }));

        for part_confirm in confirm.partition_confirms.iter() {
            fetches.push(FetchPartition::new(
                confirm.topic.as_ref(),
                part_confirm.partition,
                part_confirm.offset.unwrap(),
            ));
        }
    }

    // now fetch the messages back and verify that they are the correct
    // messages
    let fetch_resps = client.fetch_messages(fetches).unwrap();
    let messages = flatten_fetched_messages(&fetch_resps);

    let correct_messages = vec![
        (TEST_TOPIC_NAME, 0, "a".as_bytes()),
        (TEST_TOPIC_NAME, 1, "b".as_bytes()),
        (TEST_TOPIC_NAME_2, 0, "c".as_bytes()),
        (TEST_TOPIC_NAME_2, 1, "d".as_bytes()),
    ];

    assert!(correct_messages.into_iter().all(|c_msg| {
        messages.contains(&c_msg)
    }));
}

#[test]
fn test_commit_offset() {
    let mut client = new_ready_kafka_client();

    for &(partition, offset) in
        &[
            (TEST_TOPIC_PARTITIONS[0], 100),
            (TEST_TOPIC_PARTITIONS[1], 200),
            (TEST_TOPIC_PARTITIONS[0], 300),
            (TEST_TOPIC_PARTITIONS[1], 400),
            (TEST_TOPIC_PARTITIONS[0], 500),
            (TEST_TOPIC_PARTITIONS[1], 600),
        ]
    {

        client
            .commit_offset(TEST_GROUP_NAME, TEST_TOPIC_NAME, partition, offset)
            .unwrap();

        let partition_offsets: HashSet<PartitionOffset> = client
            .fetch_group_topic_offsets(TEST_GROUP_NAME, TEST_TOPIC_NAME)
            .unwrap()
            .into_iter()
            .collect();

        let correct_partition_offset = PartitionOffset {
            partition: partition,
            offset: offset,
        };

        assert!(partition_offsets.contains(&correct_partition_offset));
    }
}
<commit_msg>Test KafkaClient::commit_offsets<commit_after>/// Still to test:
///
/// * compression
/// * secure connections

use super::*;
use std::collections::HashSet;
use std::time::Duration;
use kafka::client::{KafkaClient, CommitOffset, PartitionOffset, FetchPartition, ProduceMessage,
                    RequiredAcks};
use kafka::client::fetch::Response;

fn flatten_fetched_messages(resps: &Vec<Response>) -> Vec<(&str, i32, &[u8])> {
    let mut messages = Vec::new();

    for resp in resps {
        for topic in resp.topics() {
            for partition in topic.partitions() {
                for msg in partition.data().as_ref().unwrap().messages() {
                    messages.push((topic.topic(), partition.partition(), msg.value));
                }
            }
        }
    }

    messages
}

#[test]
fn test_kafka_client_load_metadata() {
    let hosts = vec![LOCAL_KAFKA_BOOTSTRAP_HOST.to_owned()];
    let client_id = "test-id".to_string();
    let mut client = KafkaClient::new(hosts.clone());
    client.set_client_id(client_id.clone());
    client.load_metadata_all().unwrap();

    let topics = client.topics();

    // sanity checks
    assert_eq!(hosts.as_ref() as &[String], client.hosts());
    assert_eq!(&client_id, client.client_id());

    // names
    let topic_names: HashSet<&str> = topics.names()
        // don't count the consumer offsets internal topic
        .filter(|name| *name != KAFKA_CONSUMER_OFFSETS_TOPIC_NAME)
        .collect();
    let mut correct_topic_names: HashSet<&str> = HashSet::new();
    correct_topic_names.insert(TEST_TOPIC_NAME);
    correct_topic_names.insert(TEST_TOPIC_NAME_2);

    assert_eq!(correct_topic_names, topic_names);

    // partitions
    let mut topic_partitions = topics.partitions(TEST_TOPIC_NAME).unwrap().available_ids();
    let mut correct_topic_partitions = TEST_TOPIC_PARTITIONS.to_vec();
    assert_eq!(correct_topic_partitions, topic_partitions);

    topic_partitions = topics
        .partitions(TEST_TOPIC_NAME_2)
        .unwrap()
        .available_ids();
    correct_topic_partitions = TEST_TOPIC_PARTITIONS.to_vec();
    assert_eq!(correct_topic_partitions, topic_partitions);
}

#[test]
fn test_produce_fetch_messages() {
    let mut client = new_ready_kafka_client();

    // first send the messages and verify correct confirmation responses
    // from kafka
    let req = vec![
        ProduceMessage::new(TEST_TOPIC_NAME, 0, None, Some("a".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME, 1, None, Some("b".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME_2, 0, None, Some("c".as_bytes())),
        ProduceMessage::new(TEST_TOPIC_NAME_2, 1, None, Some("d".as_bytes())),
    ];

    let resp = client
        .produce_messages(RequiredAcks::All, Duration::from_millis(1000), req)
        .unwrap();

    assert_eq!(2, resp.len());

    // need to keep track of the offsets so we can fetch them next
    let mut fetches = Vec::new();

    for confirm in &resp {
        assert!(confirm.topic == TEST_TOPIC_NAME || confirm.topic == TEST_TOPIC_NAME_2);
        assert_eq!(2, confirm.partition_confirms.len());

        assert!(confirm.partition_confirms.iter().any(|part_confirm| {
            part_confirm.partition == 0 && part_confirm.offset.is_ok()
        }));

        assert!(confirm.partition_confirms.iter().any(|part_confirm| {
            part_confirm.partition == 1 && part_confirm.offset.is_ok()
        }));

        for part_confirm in confirm.partition_confirms.iter() {
            fetches.push(FetchPartition::new(
                confirm.topic.as_ref(),
                part_confirm.partition,
                part_confirm.offset.unwrap(),
            ));
        }
    }

    // now fetch the messages back and verify that they are the correct
    // messages
    let fetch_resps = client.fetch_messages(fetches).unwrap();
    let messages = flatten_fetched_messages(&fetch_resps);

    let correct_messages = vec![
        (TEST_TOPIC_NAME, 0, "a".as_bytes()),
        (TEST_TOPIC_NAME, 1, "b".as_bytes()),
        (TEST_TOPIC_NAME_2, 0, "c".as_bytes()),
        (TEST_TOPIC_NAME_2, 1, "d".as_bytes()),
    ];

    assert!(correct_messages.into_iter().all(|c_msg| {
        messages.contains(&c_msg)
    }));
}

#[test]
fn test_commit_offset() {
    let mut client = new_ready_kafka_client();

    for &(partition, offset) in
        &[
            (TEST_TOPIC_PARTITIONS[0], 100),
            (TEST_TOPIC_PARTITIONS[1], 200),
            (TEST_TOPIC_PARTITIONS[0], 300),
            (TEST_TOPIC_PARTITIONS[1], 400),
            (TEST_TOPIC_PARTITIONS[0], 500),
            (TEST_TOPIC_PARTITIONS[1], 600),
        ]
    {

        client
            .commit_offset(TEST_GROUP_NAME, TEST_TOPIC_NAME, partition, offset)
            .unwrap();

        let partition_offsets: HashSet<PartitionOffset> = client
            .fetch_group_topic_offsets(TEST_GROUP_NAME, TEST_TOPIC_NAME)
            .unwrap()
            .into_iter()
            .collect();

        let correct_partition_offset = PartitionOffset {
            partition: partition,
            offset: offset,
        };

        assert!(partition_offsets.contains(&correct_partition_offset));
    }
}

#[test]
fn test_commit_offsets() {
    let mut client = new_ready_kafka_client();

    let commits = [
        [
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[0],
                offset: 100,
            },
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[1],
                offset: 200,
            },
        ],
        [
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[0],
                offset: 300,
            },
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[1],
                offset: 400,
            },
        ],
        [
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[0],
                offset: 500,
            },
            CommitOffset {
                topic: TEST_TOPIC_NAME,
                partition: TEST_TOPIC_PARTITIONS[1],
                offset: 600,
            },
        ],
    ];

    for commit_pair in &commits {

        client.commit_offsets(TEST_GROUP_NAME, commit_pair).unwrap();

        let partition_offsets: HashSet<PartitionOffset> = client
            .fetch_group_topic_offsets(TEST_GROUP_NAME, TEST_TOPIC_NAME)
            .unwrap()
            .into_iter()
            .collect();

        println!("partition_offsets: {:?}", partition_offsets);

        let correct_partition_offsets: HashSet<PartitionOffset> = vec![
            PartitionOffset {
                partition: commit_pair[0].partition,
                offset: commit_pair[0].offset,
            },
            PartitionOffset {
                partition: commit_pair[1].partition,
                offset: commit_pair[1].offset,
            },
        ].into_iter()
            .collect();

        assert_eq!(correct_partition_offsets, partition_offsets);
    }
}
<|endoftext|>


<commit_before>use core::mem;
use core::ptr::Unique;
use spin::Mutex;
use volatile::Volatile;

use crate::memory::prelude::{PhysAddr, VirtAddr};

const SCREEN_HEIGHT: usize = 25;
const SCREEN_WIDTH: usize = 80;
pub const VGA_BUFFER_ADDR_U64: u64 = 0xb8000;
pub const VGA_BUFFER_PHYSADDR: PhysAddr = unsafe { PhysAddr::new_unchecked(VGA_BUFFER_ADDR_U64) };
pub const VGA_BUFFER_VIRTADDR: VirtAddr =
    unsafe { VirtAddr::new_unchecked_raw(VGA_BUFFER_ADDR_U64) };

/// A VGA color
#[allow(dead_code)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

/// Color of single cell, back- and foreground
#[derive(Clone, Copy)]
pub struct CellColor(u8);

impl CellColor {
    pub const fn new(foreground: Color, background: Color) -> CellColor {
        CellColor((background as u8) << 4 | (foreground as u8))
    }

    pub fn foreground(self) -> Color {
        unsafe { mem::transmute::<u8, Color>(self.0 & 0xf) }
    }

    pub fn background(self) -> Color {
        unsafe { mem::transmute::<u8, Color>((self.0 & 0xf0) >> 4) }
    }

    pub fn invert(self) -> CellColor {
        CellColor::new(self.background(), self.foreground())
    }
}

/// Character cell: one character and color in screen
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct CharCell {
    pub character: u8,
    pub color: CellColor,
}

/// A Virtual tty buffer
pub struct Buffer {
    pub chars: [[Volatile<CharCell>; SCREEN_WIDTH]; SCREEN_HEIGHT],
}

/// Cursor
pub struct Cursor {
    /// Current row
    pub row: usize,
    /// Current column
    pub col: usize,
}

impl Cursor {
    /// Next character
    pub fn next(&mut self) {
        if self.col < SCREEN_WIDTH {
            self.col += 1;
        }
    }

    /// Previous character
    /// if row is true, goes to previous line if needed
    pub fn prev(&mut self, row: bool) {
        if self.col > 0 {
            self.col -= 1;
        } else if row && self.row > 0 {
            self.row -= 1;
            self.col = SCREEN_WIDTH - 1;
        }
    }

    /// New line
    /// # Returns
    /// true if terminal should be scrolled
    pub fn newline(&mut self) -> bool {
        self.col = 0;
        if self.row < SCREEN_HEIGHT - 1 {
            self.row += 1;
            false
        } else {
            true
        }
    }

    /// Set position
    pub fn set_position(&mut self, row: usize, col: usize) {
        assert!(row < SCREEN_HEIGHT);
        assert!(col < SCREEN_WIDTH);

        self.row = row;
        self.col = col;
    }

    pub fn position(&self) -> (usize, usize) {
        (self.row, self.col)
    }
}

/// Terminal: an interface to hardware terminal
pub struct Terminal {
    output_color: CellColor,
    cursor: Cursor,
    buffer: Unique<Buffer>,
}

impl Terminal {
    /// Init terminal
    pub fn reset(&mut self) {
        self.output_color = CellColor::new(Color::White, Color::Black);
        self.clear();
    }

    /// Clear screen
    pub fn clear(&mut self) {
        self.cursor.set_position(0, 0);
        let clear_color = self.output_color;
        let buffer = self.get_buffer();
        for col in 0..SCREEN_WIDTH {
            for row in 0..SCREEN_HEIGHT {
                buffer.chars[row][col].write(CharCell {
                    character: b' ',
                    color: clear_color,
                });
            }
        }
    }

    /// Write single byte to terminal's stdout
    pub fn write_byte(&mut self, byte: u8) {
        assert!(byte != 0);

        if byte == b'\n' {
            self.newline();
        } else if byte == 0x8 {
            // ASCII backspace
            self.backspace();
        } else {
            assert!(self.cursor.col < SCREEN_WIDTH);

            let color = self.output_color;
            let (row, col) = self.cursor.position();

            self.get_buffer().chars[row][col].write(CharCell {
                character: byte,
                color,
            });

            self.cursor.next();

            if self.cursor.col >= SCREEN_WIDTH {
                self.newline();
            }
        }
    }

    /// Get color
    pub fn get_color(&mut self) -> CellColor {
        self.output_color
    }

    /// Set color
    pub fn set_color(&mut self, color: CellColor) {
        self.output_color = color;
    }

    /// Newline
    pub fn newline(&mut self) {
        let scroll = self.cursor.newline();
        if scroll {
            self.scroll_line();
        }
    }

    /// Backspace
    pub fn backspace(&mut self) {
        self.cursor.prev(true);
        self.write_byte(b' ');
        self.cursor.prev(true);
    }

    /// Scroll up one line
    fn scroll_line(&mut self) {
        // move lines up
        for row in 0..(SCREEN_HEIGHT - 1) {
            for col in 0..SCREEN_WIDTH {
                let new_value = self.get_buffer().chars[row + 1][col].read();
                self.get_buffer().chars[row][col].write(new_value);
            }
        }
        // clear the bottom line
        for col in 0..SCREEN_WIDTH {
            let color = self.output_color;
            self.get_buffer().chars[SCREEN_HEIGHT - 1][col].write(CharCell {
                character: b' ',
                color,
            });
        }
    }

    /// Get pointer to memory buffer
    fn get_buffer(&mut self) -> &mut Buffer {
        unsafe { self.buffer.as_mut() }
    }
}

/// Allow formatting
impl ::core::fmt::Write for Terminal {
    fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
        for byte in s.bytes() {
            self.write_byte(byte)
        }
        Ok(()) // Success. Always.
    }
}

/// Actual print function. This should only be externally called using the rprint! and rprintln! macros.
pub fn print(fmt: ::core::fmt::Arguments) {
    use core::fmt::Write;
    TERMINAL
        .try_lock()
        .expect("TERMINAL LOCKED")
        .write_fmt(fmt)
        .unwrap();
}

/// Print with color
pub fn printc(fmt: ::core::fmt::Arguments, color: CellColor) {
    use core::fmt::Write;
    let mut t = TERMINAL.try_lock().expect("TERMINAL LOCKED");
    let old_color = t.get_color();
    t.set_color(color);
    t.write_fmt(fmt).unwrap();
    t.set_color(old_color);
}

// Create static pointer mutex with spinlock to make TERMINAL thread-safe
pub static TERMINAL: Mutex<Terminal> = Mutex::new(Terminal {
    output_color: CellColor::new(Color::White, Color::Black),
    cursor: Cursor { row: 0, col: 0 },
    buffer: unsafe { Unique::new_unchecked(VGA_BUFFER_VIRTADDR.as_mut_ptr_unchecked()) },
});

/// "Raw" output macros
macro_rules! rprint {
    ($($arg:tt)*) => ({
        $crate::driver::vga_buffer::print(format_args!($($arg)*));
    });
}
macro_rules! rprintln {
    ($fmt:expr) => (rprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (rprint!(concat!($fmt, "\n"), $($arg)*));
}
macro_rules! rprintc {
    ($fg:expr, $bg:expr ; $($arg:tt)*) => ({
        use $crate::driver::vga_buffer::CellColor;
        $crate::driver::vga_buffer::printc(
            format_args!($($arg)*),
            CellColor::new($fg, $bg)
        );
    });

}
macro_rules! rprintlnc {
    ($fg:expr, $bg:expr ; $fmt:expr, $($arg:tt)*) => (rprintc!( $fg, $bg; concat!($fmt, "\n"), $($arg)*));
    ($fg:expr ; $fmt:expr, $($arg:tt)*) => (rprintlnc!($fg, Color::Black ; $fmt, $($arg)*));
    ($fg:expr, $bg:expr ; $fmt:expr) => (rprintc!( $fg, $bg ; concat!($fmt, "\n")));
    ($fg:expr ; $fmt:expr) => (rprintlnc!($fg, Color::Black ; $fmt));
}
macro_rules! rreset {
    () => {{
        $crate::driver::vga_buffer::TERMINAL
            .try_lock()
            .expect("TERMINAL LOCKED")
            .reset();
    }};
}
macro_rules! rforce_unlock {
    () => {{
        $crate::driver::vga_buffer::TERMINAL.force_unlock();
    }};
}
macro_rules! panic_indicator {
    ($x:expr) => ({
        asm!(concat!("mov eax, ", stringify!($x), "; mov [0xb809c], eax") ::: "eax", "memory" : "volatile", "intel");
    });
    () => ({
        panic_indicator!(0x4f214f70);   // !p
    });
}
<commit_msg>Add reset_line command to vga_buffer<commit_after>use core::mem;
use core::ptr::Unique;
use spin::Mutex;
use volatile::Volatile;

use crate::memory::prelude::{PhysAddr, VirtAddr};

const SCREEN_HEIGHT: usize = 25;
const SCREEN_WIDTH: usize = 80;
pub const VGA_BUFFER_ADDR_U64: u64 = 0xb8000;
pub const VGA_BUFFER_PHYSADDR: PhysAddr = unsafe { PhysAddr::new_unchecked(VGA_BUFFER_ADDR_U64) };
pub const VGA_BUFFER_VIRTADDR: VirtAddr =
    unsafe { VirtAddr::new_unchecked_raw(VGA_BUFFER_ADDR_U64) };

/// A VGA color
#[allow(dead_code)]
#[repr(u8)]
pub enum Color {
    Black = 0,
    Blue = 1,
    Green = 2,
    Cyan = 3,
    Red = 4,
    Magenta = 5,
    Brown = 6,
    LightGray = 7,
    DarkGray = 8,
    LightBlue = 9,
    LightGreen = 10,
    LightCyan = 11,
    LightRed = 12,
    Pink = 13,
    Yellow = 14,
    White = 15,
}

/// Color of single cell, back- and foreground
#[derive(Clone, Copy)]
pub struct CellColor(u8);

impl CellColor {
    pub const fn new(foreground: Color, background: Color) -> CellColor {
        CellColor((background as u8) << 4 | (foreground as u8))
    }

    pub fn foreground(self) -> Color {
        unsafe { mem::transmute::<u8, Color>(self.0 & 0xf) }
    }

    pub fn background(self) -> Color {
        unsafe { mem::transmute::<u8, Color>((self.0 & 0xf0) >> 4) }
    }

    pub fn invert(self) -> CellColor {
        CellColor::new(self.background(), self.foreground())
    }
}

/// Character cell: one character and color in screen
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct CharCell {
    pub character: u8,
    pub color: CellColor,
}

/// A Virtual tty buffer
pub struct Buffer {
    pub chars: [[Volatile<CharCell>; SCREEN_WIDTH]; SCREEN_HEIGHT],
}

/// Cursor
pub struct Cursor {
    /// Current row
    pub row: usize,
    /// Current column
    pub col: usize,
}

impl Cursor {
    /// Next character
    pub fn next(&mut self) {
        if self.col < SCREEN_WIDTH {
            self.col += 1;
        }
    }

    /// Previous character
    /// if row is true, goes to previous line if needed
    pub fn prev(&mut self, row: bool) {
        if self.col > 0 {
            self.col -= 1;
        } else if row && self.row > 0 {
            self.row -= 1;
            self.col = SCREEN_WIDTH - 1;
        }
    }

    /// New line
    /// # Returns
    /// true if terminal should be scrolled
    pub fn newline(&mut self) -> bool {
        self.col = 0;
        if self.row < SCREEN_HEIGHT - 1 {
            self.row += 1;
            false
        } else {
            true
        }
    }

    /// Set position
    pub fn set_position(&mut self, row: usize, col: usize) {
        assert!(row < SCREEN_HEIGHT);
        assert!(col < SCREEN_WIDTH);

        self.row = row;
        self.col = col;
    }

    pub fn position(&self) -> (usize, usize) {
        (self.row, self.col)
    }
}

/// Terminal: an interface to hardware terminal
pub struct Terminal {
    output_color: CellColor,
    cursor: Cursor,
    buffer: Unique<Buffer>,
}

impl Terminal {
    /// Init terminal
    pub fn reset(&mut self) {
        self.output_color = CellColor::new(Color::White, Color::Black);
        self.clear();
    }

    /// Clear screen
    pub fn clear(&mut self) {
        self.cursor.set_position(0, 0);
        let clear_color = self.output_color;
        let buffer = self.get_buffer();
        for col in 0..SCREEN_WIDTH {
            for row in 0..SCREEN_HEIGHT {
                buffer.chars[row][col].write(CharCell {
                    character: b' ',
                    color: clear_color,
                });
            }
        }
    }

    /// Clear the current line
    pub fn clear_line(&mut self) {
        self.cursor.col = 0;
        let row = self.cursor.row;
        let clear_color = self.output_color;
        let buffer = self.get_buffer();
        for col in 0..SCREEN_WIDTH {
            buffer.chars[row][col].write(CharCell {
                character: b' ',
                color: clear_color,
            });
        }
    }

    /// Write single byte to terminal's stdout
    pub fn write_byte(&mut self, byte: u8) {
        assert!(byte != 0);

        if byte == b'\n' {
            self.newline();
        } else if byte == 0x8 {
            // ASCII backspace
            self.backspace();
        } else {
            assert!(self.cursor.col < SCREEN_WIDTH);

            let color = self.output_color;
            let (row, col) = self.cursor.position();

            self.get_buffer().chars[row][col].write(CharCell {
                character: byte,
                color,
            });

            self.cursor.next();

            if self.cursor.col >= SCREEN_WIDTH {
                self.newline();
            }
        }
    }

    /// Get color
    pub fn get_color(&mut self) -> CellColor {
        self.output_color
    }

    /// Set color
    pub fn set_color(&mut self, color: CellColor) {
        self.output_color = color;
    }

    /// Newline
    pub fn newline(&mut self) {
        let scroll = self.cursor.newline();
        if scroll {
            self.scroll_line();
        }
    }

    /// Backspace
    pub fn backspace(&mut self) {
        self.cursor.prev(true);
        self.write_byte(b' ');
        self.cursor.prev(true);
    }

    /// Scroll up one line
    fn scroll_line(&mut self) {
        // move lines up
        for row in 0..(SCREEN_HEIGHT - 1) {
            for col in 0..SCREEN_WIDTH {
                let new_value = self.get_buffer().chars[row + 1][col].read();
                self.get_buffer().chars[row][col].write(new_value);
            }
        }
        // clear the bottom line
        for col in 0..SCREEN_WIDTH {
            let color = self.output_color;
            self.get_buffer().chars[SCREEN_HEIGHT - 1][col].write(CharCell {
                character: b' ',
                color,
            });
        }
    }

    /// Get pointer to memory buffer
    fn get_buffer(&mut self) -> &mut Buffer {
        unsafe { self.buffer.as_mut() }
    }
}

/// Allow formatting
impl ::core::fmt::Write for Terminal {
    fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
        for byte in s.bytes() {
            self.write_byte(byte)
        }
        Ok(()) // Success. Always.
    }
}

/// Actual print function. This should only be externally called using the rprint! and rprintln! macros.
pub fn print(fmt: ::core::fmt::Arguments) {
    use core::fmt::Write;
    TERMINAL
        .try_lock()
        .expect("TERMINAL LOCKED")
        .write_fmt(fmt)
        .unwrap();
}

/// Print with color
pub fn printc(fmt: ::core::fmt::Arguments, color: CellColor) {
    use core::fmt::Write;
    let mut t = TERMINAL.try_lock().expect("TERMINAL LOCKED");
    let old_color = t.get_color();
    t.set_color(color);
    t.write_fmt(fmt).unwrap();
    t.set_color(old_color);
}

// Create static pointer mutex with spinlock to make TERMINAL thread-safe
pub static TERMINAL: Mutex<Terminal> = Mutex::new(Terminal {
    output_color: CellColor::new(Color::White, Color::Black),
    cursor: Cursor { row: 0, col: 0 },
    buffer: unsafe { Unique::new_unchecked(VGA_BUFFER_VIRTADDR.as_mut_ptr_unchecked()) },
});

/// "Raw" output macros
macro_rules! rprint {
    ($($arg:tt)*) => ({
        $crate::driver::vga_buffer::print(format_args!($($arg)*));
    });
}
macro_rules! rprintln {
    ($fmt:expr) => (rprint!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (rprint!(concat!($fmt, "\n"), $($arg)*));
}
macro_rules! rprintc {
    ($fg:expr, $bg:expr ; $($arg:tt)*) => ({
        use $crate::driver::vga_buffer::CellColor;
        $crate::driver::vga_buffer::printc(
            format_args!($($arg)*),
            CellColor::new($fg, $bg)
        );
    });

}
macro_rules! rprintlnc {
    ($fg:expr, $bg:expr ; $fmt:expr, $($arg:tt)*) => (rprintc!( $fg, $bg; concat!($fmt, "\n"), $($arg)*));
    ($fg:expr ; $fmt:expr, $($arg:tt)*) => (rprintlnc!($fg, Color::Black ; $fmt, $($arg)*));
    ($fg:expr, $bg:expr ; $fmt:expr) => (rprintc!( $fg, $bg ; concat!($fmt, "\n")));
    ($fg:expr ; $fmt:expr) => (rprintlnc!($fg, Color::Black ; $fmt));
}
macro_rules! rreset {
    () => {{
        $crate::driver::vga_buffer::TERMINAL
            .try_lock()
            .expect("TERMINAL LOCKED")
            .reset();
    }};
}
macro_rules! rclearline {
    () => {{
        $crate::driver::vga_buffer::TERMINAL
            .try_lock()
            .expect("TERMINAL LOCKED")
            .clear_line();
    }};
}
macro_rules! rforce_unlock {
    () => {{
        $crate::driver::vga_buffer::TERMINAL.force_unlock();
    }};
}
macro_rules! panic_indicator {
    ($x:expr) => ({
        asm!(concat!("mov eax, ", stringify!($x), "; mov [0xb809c], eax") ::: "eax", "memory" : "volatile", "intel");
    });
    () => ({
        panic_indicator!(0x4f214f70);   // !p
    });
}
<|endoftext|>


<commit_before>//! Simple example that echos recevied serial traffic to stdout
extern crate mio;
extern crate mio_serial;

#[cfg(unix)]
use mio::unix::UnixReady;
use mio::{Events, Poll, PollOpt, Ready, Token};
use std::env;
use std::io::Read;
use std::str;

const SERIAL_TOKEN: Token = Token(0);

#[cfg(unix)]
const DEFAULT_TTY: &str = "/dev/ttyUSB0";
#[cfg(windows)]
const DEFAULT_TTY: &str = "COM1";

#[cfg(unix)]
fn ready_of_interest() -> Ready {
    Ready::readable() | UnixReady::hup() | UnixReady::error()
}
#[cfg(windows)]
fn ready_of_interest() -> Ready {
    Ready::readable()
}

#[cfg(unix)]
fn is_closed(state: Ready) -> bool {
    state.contains(UnixReady::hup() | UnixReady::error())
}
#[cfg(windows)]
fn is_closed(state: Ready) -> bool {
    false
}

pub fn main() {
    let mut args = env::args();
    let tty_path = args.nth(1).unwrap_or_else(|| DEFAULT_TTY.into());

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    // Create the listener
    let settings = mio_serial::SerialPortSettings::default();

    println!("Opening {} at 9600,8N1", tty_path);
    let mut rx = mio_serial::Serial::from_path(&tty_path, &settings).unwrap();

    poll.register(&rx, SERIAL_TOKEN, ready_of_interest(), PollOpt::edge())
        .unwrap();

    let mut rx_buf = [0u8; 1024];

    'outer: loop {
        poll.poll(&mut events, None).unwrap();

        if events.is_empty() {
            println!("Read timed out!");
            continue;
        }

        for event in events.iter() {
            match event.token() {
                SERIAL_TOKEN => {
                    let ready = event.readiness();
                    if is_closed(ready) {
                        println!("Quitting due to event: {:?}", ready);
                        break 'outer;
                    }
                    if ready.is_readable() {
                        match rx.read(&mut rx_buf) {
                            Ok(b) => match b {
                                b if b > 0 => {
                                    println!("{:?}", String::from_utf8_lossy(&rx_buf[..b]))
                                }
                                _ => println!("Read would have blocked."),
                            },
                            Err(e) => println!("Error:  {}", e),
                        }
                    }
                }
                t => unreachable!("Unexpected token: {:?}", t),
            }
        }
    }
}
<commit_msg>docs: Example improvements - handle WouldBlock correctly, typo<commit_after>//! Simple example that echoes received serial traffic to stdout
extern crate mio;
extern crate mio_serial;

#[cfg(unix)]
use mio::unix::UnixReady;
use mio::{Events, Poll, PollOpt, Ready, Token};
use std::env;
use std::io;
use std::io::Read;
use std::str;

const SERIAL_TOKEN: Token = Token(0);

#[cfg(unix)]
const DEFAULT_TTY: &str = "/dev/ttyUSB0";
#[cfg(windows)]
const DEFAULT_TTY: &str = "COM1";

#[cfg(unix)]
fn ready_of_interest() -> Ready {
    Ready::readable() | UnixReady::hup() | UnixReady::error()
}

#[cfg(windows)]
fn ready_of_interest() -> Ready {
    Ready::readable()
}

#[cfg(unix)]
fn is_closed(state: Ready) -> bool {
    state.contains(UnixReady::hup() | UnixReady::error())
}

#[cfg(windows)]
fn is_closed(state: Ready) -> bool {
    false
}

pub fn main() {
    let mut args = env::args();
    let tty_path = args.nth(1).unwrap_or_else(|| DEFAULT_TTY.into());

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    // Create the listener
    let settings = mio_serial::SerialPortSettings::default();

    println!("Opening {} at 9600,8N1", tty_path);
    let mut rx = mio_serial::Serial::from_path(&tty_path, &settings).unwrap();

    poll.register(&rx, SERIAL_TOKEN, ready_of_interest(), PollOpt::edge())
        .unwrap();

    let mut rx_buf = [0u8; 1024];

    'outer: loop {
        if let Err(ref e) = poll.poll(&mut events, None) {
            println!("poll failed: {}", e);
            break;
        }

        if events.is_empty() {
            println!("Read timed out!");
            continue;
        }

        for event in events.iter() {
            match event.token() {
                SERIAL_TOKEN => {
                    let ready = event.readiness();
                    if is_closed(ready) {
                        println!("Quitting due to event: {:?}", ready);
                        break 'outer;
                    }
                    if ready.is_readable() {
                        // With edge triggered events, we must perform reading until we receive a WouldBlock.
                        // See https://docs.rs/mio/0.6.16/mio/struct.Poll.html for details.
                        loop {
                            match rx.read(&mut rx_buf) {
                                Ok(count) => {
                                    println!("{:?}", String::from_utf8_lossy(&rx_buf[..count]))
                                }
                                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                                    break;
                                }
                                Err(ref e) => {
                                    println!("Quitting due to read error: {}", e);
                                    break 'outer;
                                }
                            }
                        }
                    }
                }
                t => unreachable!("Unexpected token: {:?}", t),
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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
//
//     https://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::errors::{CompilationError, Error};
use crate::{EvalContext, EvalContextOutputs, EvalOutputs};

/// A higher level interface to EvalContext. A bit closer to a Repl. Provides commands (start with
/// ':') that alter context state or print information.
pub struct CommandContext {
    print_timings: bool,
    eval_context: EvalContext,
    last_errors: Vec<CompilationError>,
}

impl CommandContext {
    pub fn new() -> Result<(CommandContext, EvalContextOutputs), Error> {
        let (eval_context, eval_context_outputs) = EvalContext::new()?;
        let command_context = CommandContext {
            print_timings: false,
            eval_context,
            last_errors: Vec::new(),
        };
        Ok((command_context, eval_context_outputs))
    }

    pub fn execute(&mut self, to_run: &str) -> Result<EvalOutputs, Error> {
        use std::time::Instant;
        let start = Instant::now();
        let result = if to_run.is_empty() {
            Ok(EvalOutputs::new())
        } else if to_run.starts_with(':') {
            self.process_command(to_run)
        } else {
            self.eval_context.eval(to_run)
        };
        let duration = start.elapsed();
        match result {
            Ok(mut m) => {
                if self.print_timings {
                    m.timing = Some(duration);
                }
                Ok(m)
            }
            Err(Error::CompilationErrors(errors)) => {
                self.last_errors = errors.clone();
                Err(Error::CompilationErrors(errors))
            }
            x => x,
        }
    }

    fn load_config(&mut self) -> Result<EvalOutputs, Error> {
        let mut outputs = EvalOutputs::new();
        if let Some(config_dir) = dirs::config_dir() {
            let config_file = config_dir.join("evcxr").join("init.evcxr");
            if config_file.exists() {
                println!("Loading startup commands from {:?}", config_file);
                let contents = std::fs::read_to_string(config_file)?;
                for line in contents.lines() {
                    outputs.merge(self.execute(line)?);
                }
            }
        }
        Ok(outputs)
    }

    fn process_command(&mut self, line: &str) -> Result<EvalOutputs, Error> {
        use regex::Regex;
        lazy_static! {
            static ref ADD_DEP_RE: Regex = Regex::new(":dep ([^= ]+) *= *(.+)").unwrap();
        }
        lazy_static! {
            static ref OPT_RE: Regex = Regex::new(":opt( (0|1|2))?").unwrap();
        }
        lazy_static! {
            static ref PRESERVE_VARS_RE: Regex =
                Regex::new(":preserve_vars_on_panic (0|1)").unwrap();
        }
        if line == ":internal_debug" {
            let debug_mode = !self.eval_context.debug_mode();
            self.eval_context.set_debug_mode(debug_mode);
            text_output(format!("Internals debugging: {}", debug_mode))
        } else if line == ":load_config" {
            self.load_config()
        } else if line == ":vars" {
            let mut outputs = EvalOutputs::new();
            outputs
                .content_by_mime_type
                .insert("text/plain".to_owned(), self.vars_as_text());
            outputs
                .content_by_mime_type
                .insert("text/html".to_owned(), self.vars_as_html());
            Ok(outputs)
        } else if let Some(captures) = PRESERVE_VARS_RE.captures(line) {
            self.eval_context.preserve_vars_on_panic = &captures[1] == "1";
            text_output(format!(
                "Preserve vars on panic: {}",
                self.eval_context.preserve_vars_on_panic
            ))
        } else if line == ":clear" {
            self.eval_context.clear().map(|_| EvalOutputs::new())
        } else if let Some(captures) = ADD_DEP_RE.captures(line) {
            self.eval_context
                .add_extern_crate(captures[1].to_owned(), captures[2].to_owned())
        } else if line == ":last_compile_dir" {
            text_output(format!("{:?}", self.eval_context.last_compile_dir()))
        } else if let Some(captures) = OPT_RE.captures(line) {
            let new_level = if let Some(n) = captures.get(2) {
                n.as_str()
            } else if self.eval_context.opt_level() == "2" {
                "0"
            } else {
                "2"
            };
            self.eval_context.set_opt_level(new_level)?;
            text_output(format!("Optimization: {}", self.eval_context.opt_level()))
        } else if line == ":timing" {
            self.print_timings = !self.print_timings;
            text_output(format!("Timing: {}", self.print_timings))
        } else if line == ":time_passes" {
            self.eval_context
                .set_time_passes(!self.eval_context.time_passes());
            text_output(format!("Time passes: {}", self.eval_context.time_passes()))
        } else if line == ":explain" {
            if self.last_errors.is_empty() {
                bail!("No last error to explain");
            } else {
                let mut all_explanations = String::new();
                for error in &self.last_errors {
                    if let Some(explanation) = error.explanation() {
                        all_explanations.push_str(explanation);
                    } else {
                        bail!("Sorry, last error has no explanation");
                    }
                }
                text_output(all_explanations)
            }
        } else if line == ":last_error_json" {
            let mut errors_out = String::new();
            for error in &self.last_errors {
                use std::fmt::Write;
                write!(&mut errors_out, "{}", error.json)?;
                errors_out.push('\n');
            }
            bail!(errors_out);
        } else if line == ":help" {
            text_output(
                ":vars             List bound variables and their types\n\
                 :opt [level]      Toggle/set optimization level\n\
                 :explain          Print explanation of last error\n\
                 :clear            Clear all state, keeping compilation cache\n\
                 :dep              Add dependency. e.g. :dep regex = \"1.0\"\n\
                 :preserve_vars_on_panic [0|1]  Try to keep vars on panic\n\n\
                 Mostly for development / debugging purposes:\n\
                 :last_compile_dir Print the directory in which we last compiled\n\
                 :timing           Toggle printing of how long evaluations take\n\
                 :last_error_json  Print the last compilation error as JSON (for debugging)\n\
                 :time_passes      Toggle printing of rustc pass times (requires nightly)\n\
                 :internal_debug   Toggle various internal debugging code",
            )
        } else {
            bail!("Unrecognised command {}", line);
        }
    }

    fn vars_as_text(&self) -> String {
        let mut out = String::new();
        for (var, ty) in self.eval_context.variables_and_types() {
            out.push_str(var);
            out.push_str(": ");
            out.push_str(ty);
            out.push_str("\n");
        }
        out
    }

    fn vars_as_html(&self) -> String {
        let mut out = String::new();
        out.push_str("

Variable	Type
");
        for (var, ty) in self.eval_context.variables_and_types() {
            out.push_str("
");
            html_escape(var, &mut out);
            out.push_str("	");
            html_escape(ty, &mut out);
            out.push_str("
");
        }
        out.push_str("
");
        out
    }
}

fn html_escape(input: &str, out: &mut String) {
    for ch in input.chars() {
        match ch {
            '<' => out.push_str("<"),
            '>' => out.push_str(">"),
            x => out.push(x),
        }
    }
}

fn text_output<T: Into<String>>(text: T) -> Result<EvalOutputs, Error> {
    let mut outputs = EvalOutputs::new();
    let mut content = text.into();
    content.push('\n');
    outputs
        .content_by_mime_type
        .insert("text/plain".to_owned(), content);
    Ok(outputs)
}
<commit_msg>Add a :version command. Fixes #58<commit_after>// Copyright 2018 Google Inc.
//
// 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
//
//     https://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::errors::{CompilationError, Error};
use crate::{EvalContext, EvalContextOutputs, EvalOutputs};

/// A higher level interface to EvalContext. A bit closer to a Repl. Provides commands (start with
/// ':') that alter context state or print information.
pub struct CommandContext {
    print_timings: bool,
    eval_context: EvalContext,
    last_errors: Vec<CompilationError>,
}

impl CommandContext {
    pub fn new() -> Result<(CommandContext, EvalContextOutputs), Error> {
        let (eval_context, eval_context_outputs) = EvalContext::new()?;
        let command_context = CommandContext {
            print_timings: false,
            eval_context,
            last_errors: Vec::new(),
        };
        Ok((command_context, eval_context_outputs))
    }

    pub fn execute(&mut self, to_run: &str) -> Result<EvalOutputs, Error> {
        use std::time::Instant;
        let start = Instant::now();
        let result = if to_run.is_empty() {
            Ok(EvalOutputs::new())
        } else if to_run.starts_with(':') {
            self.process_command(to_run)
        } else {
            self.eval_context.eval(to_run)
        };
        let duration = start.elapsed();
        match result {
            Ok(mut m) => {
                if self.print_timings {
                    m.timing = Some(duration);
                }
                Ok(m)
            }
            Err(Error::CompilationErrors(errors)) => {
                self.last_errors = errors.clone();
                Err(Error::CompilationErrors(errors))
            }
            x => x,
        }
    }

    fn load_config(&mut self) -> Result<EvalOutputs, Error> {
        let mut outputs = EvalOutputs::new();
        if let Some(config_dir) = dirs::config_dir() {
            let config_file = config_dir.join("evcxr").join("init.evcxr");
            if config_file.exists() {
                println!("Loading startup commands from {:?}", config_file);
                let contents = std::fs::read_to_string(config_file)?;
                for line in contents.lines() {
                    outputs.merge(self.execute(line)?);
                }
            }
        }
        Ok(outputs)
    }

    fn process_command(&mut self, line: &str) -> Result<EvalOutputs, Error> {
        use regex::Regex;
        lazy_static! {
            static ref ADD_DEP_RE: Regex = Regex::new(":dep ([^= ]+) *= *(.+)").unwrap();
        }
        lazy_static! {
            static ref OPT_RE: Regex = Regex::new(":opt( (0|1|2))?").unwrap();
        }
        lazy_static! {
            static ref PRESERVE_VARS_RE: Regex =
                Regex::new(":preserve_vars_on_panic (0|1)").unwrap();
        }
        if line == ":internal_debug" {
            let debug_mode = !self.eval_context.debug_mode();
            self.eval_context.set_debug_mode(debug_mode);
            text_output(format!("Internals debugging: {}", debug_mode))
        } else if line == ":load_config" {
            self.load_config()
        } else if line == ":version" {
            text_output(env!("CARGO_PKG_VERSION"))
        } else if line == ":vars" {
            let mut outputs = EvalOutputs::new();
            outputs
                .content_by_mime_type
                .insert("text/plain".to_owned(), self.vars_as_text());
            outputs
                .content_by_mime_type
                .insert("text/html".to_owned(), self.vars_as_html());
            Ok(outputs)
        } else if let Some(captures) = PRESERVE_VARS_RE.captures(line) {
            self.eval_context.preserve_vars_on_panic = &captures[1] == "1";
            text_output(format!(
                "Preserve vars on panic: {}",
                self.eval_context.preserve_vars_on_panic
            ))
        } else if line == ":clear" {
            self.eval_context.clear().map(|_| EvalOutputs::new())
        } else if let Some(captures) = ADD_DEP_RE.captures(line) {
            self.eval_context
                .add_extern_crate(captures[1].to_owned(), captures[2].to_owned())
        } else if line == ":last_compile_dir" {
            text_output(format!("{:?}", self.eval_context.last_compile_dir()))
        } else if let Some(captures) = OPT_RE.captures(line) {
            let new_level = if let Some(n) = captures.get(2) {
                n.as_str()
            } else if self.eval_context.opt_level() == "2" {
                "0"
            } else {
                "2"
            };
            self.eval_context.set_opt_level(new_level)?;
            text_output(format!("Optimization: {}", self.eval_context.opt_level()))
        } else if line == ":timing" {
            self.print_timings = !self.print_timings;
            text_output(format!("Timing: {}", self.print_timings))
        } else if line == ":time_passes" {
            self.eval_context
                .set_time_passes(!self.eval_context.time_passes());
            text_output(format!("Time passes: {}", self.eval_context.time_passes()))
        } else if line == ":explain" {
            if self.last_errors.is_empty() {
                bail!("No last error to explain");
            } else {
                let mut all_explanations = String::new();
                for error in &self.last_errors {
                    if let Some(explanation) = error.explanation() {
                        all_explanations.push_str(explanation);
                    } else {
                        bail!("Sorry, last error has no explanation");
                    }
                }
                text_output(all_explanations)
            }
        } else if line == ":last_error_json" {
            let mut errors_out = String::new();
            for error in &self.last_errors {
                use std::fmt::Write;
                write!(&mut errors_out, "{}", error.json)?;
                errors_out.push('\n');
            }
            bail!(errors_out);
        } else if line == ":help" {
            text_output(
                ":vars             List bound variables and their types\n\
                 :opt [level]      Toggle/set optimization level\n\
                 :explain          Print explanation of last error\n\
                 :clear            Clear all state, keeping compilation cache\n\
                 :dep              Add dependency. e.g. :dep regex = \"1.0\"\n\
                 :version          Print Evcxr version\n\
                 :preserve_vars_on_panic [0|1]  Try to keep vars on panic\n\n\
                 Mostly for development / debugging purposes:\n\
                 :last_compile_dir Print the directory in which we last compiled\n\
                 :timing           Toggle printing of how long evaluations take\n\
                 :last_error_json  Print the last compilation error as JSON (for debugging)\n\
                 :time_passes      Toggle printing of rustc pass times (requires nightly)\n\
                 :internal_debug   Toggle various internal debugging code",
            )
        } else {
            bail!("Unrecognised command {}", line);
        }
    }

    fn vars_as_text(&self) -> String {
        let mut out = String::new();
        for (var, ty) in self.eval_context.variables_and_types() {
            out.push_str(var);
            out.push_str(": ");
            out.push_str(ty);
            out.push_str("\n");
        }
        out
    }

    fn vars_as_html(&self) -> String {
        let mut out = String::new();
        out.push_str("

Variable	Type
");
        for (var, ty) in self.eval_context.variables_and_types() {
            out.push_str("
");
            html_escape(var, &mut out);
            out.push_str("	");
            html_escape(ty, &mut out);
            out.push_str("
");
        }
        out.push_str("
");
        out
    }
}

fn html_escape(input: &str, out: &mut String) {
    for ch in input.chars() {
        match ch {
            '<' => out.push_str("<"),
            '>' => out.push_str(">"),
            x => out.push(x),
        }
    }
}

fn text_output<T: Into<String>>(text: T) -> Result<EvalOutputs, Error> {
    let mut outputs = EvalOutputs::new();
    let mut content = text.into();
    content.push('\n');
    outputs
        .content_by_mime_type
        .insert("text/plain".to_owned(), content);
    Ok(outputs)
}
<|endoftext|>


<commit_before>// Copyright 2020 The Evcxr Authors.
//
// 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
//
//     https://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 anyhow::{anyhow, bail, Result};
use dirs;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};

const LOGO_32X32: &[u8] = include_bytes!("../third_party/rust/rust-logo-32x32.png");
const LOGO_64X64: &[u8] = include_bytes!("../third_party/rust/rust-logo-64x64.png");
const LOGO_LICENSE: &[u8] = include_bytes!("../third_party/rust/LICENSE.md");
const KERNEL_JS: &[u8] = include_bytes!("../client/kernel.js");
const LINT_JS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.js");
const LINT_CSS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.css");
const LINT_LICENSE: &[u8] = include_bytes!("../third_party/CodeMirror/LICENSE");
const VERSION_TXT: &[u8] = include_bytes!("../client/version.txt");

pub(crate) fn install() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    fs::create_dir_all(&kernel_dir)?;
    let current_exe_path = env::current_exe()?;
    let current_exe = current_exe_path
        .to_str()
        .ok_or_else(|| anyhow!("current exe path isn't valid UTF-8"))?;
    let kernel_json = object! {
        "argv" => array![current_exe, "--control_file", "{connection_file}"],
        "display_name" => "Rust",
        "language" => "rust",
        "interrupt_mode" => "message",
    };
    let kernel_json_filename = kernel_dir.join("kernel.json");
    println!("Writing {}", kernel_json_filename.to_string_lossy());
    kernel_json.write_pretty(&mut fs::File::create(kernel_json_filename)?, 2)?;
    install_resource(&kernel_dir, "logo-32x32.png", LOGO_32X32)?;
    install_resource(&kernel_dir, "logo-64x64.png", LOGO_64X64)?;
    install_resource(&kernel_dir, "logo-LICENSE.md", LOGO_LICENSE)?;
    install_resource(&kernel_dir, "kernel.js", KERNEL_JS)?;
    install_resource(&kernel_dir, "lint.js", LINT_JS)?;
    install_resource(&kernel_dir, "lint.css", LINT_CSS)?;
    install_resource(&kernel_dir, "lint-LICENSE", LINT_LICENSE)?;
    install_resource(&kernel_dir, "version.txt", VERSION_TXT)?;
    println!("Installation complete");
    Ok(())
}

/// Checks if the current installation is out-of-date, by looking at what's in
/// version.txt. If it is out of date, then updates it.
pub(crate) fn update_if_necessary() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    let installed_version = std::fs::read(kernel_dir.join("version.txt")).unwrap_or_default();
    if installed_version != VERSION_TXT {
        install()?;
        eprintln!(
            "\n\n==================================================================\n\
            Updated Evcxr Jupyter installation. Note, updates unfortunately \n\
            won't take effect until the next time you start jupyter notebook.\n\
            ==================================================================\n"
        );
    }
    Ok(())
}

pub(crate) fn install_resource(dir: &PathBuf, filename: &str, bytes: &'static [u8]) -> Result<()> {
    let res_path = dir.join(filename);
    println!("Writing {}", res_path.to_string_lossy());
    let mut file = fs::File::create(res_path)?;
    file.write_all(bytes)?;
    Ok(())
}

pub(crate) fn uninstall() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    println!("Deleting {}", kernel_dir.to_string_lossy());
    fs::remove_dir_all(kernel_dir)?;
    println!("Uninstall complete");
    Ok(())
}

// https://jupyter-client.readthedocs.io/en/latest/kernels.html
fn get_kernel_dir() -> Result<PathBuf> {
    let jupyter_dir = if let Some(dir) = get_user_kernel_dir() {
        dir
    } else {
        bail!("Couldn't get XDG data directory");
    };
    Ok(jupyter_dir.join("kernels").join("rust"))
}

#[cfg(not(target_os = "macos"))]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().map(|data_dir| data_dir.join("jupyter"))
}

#[cfg(target_os = "macos")]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().and_then(|d| d.parent().map(|data_dir| data_dir.join("Jupyter")))
}
<commit_msg>Restore use of JUPYTER_PATH<commit_after>// Copyright 2020 The Evcxr Authors.
//
// 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
//
//     https://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 anyhow::{anyhow, bail, Result};
use dirs;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};

const LOGO_32X32: &[u8] = include_bytes!("../third_party/rust/rust-logo-32x32.png");
const LOGO_64X64: &[u8] = include_bytes!("../third_party/rust/rust-logo-64x64.png");
const LOGO_LICENSE: &[u8] = include_bytes!("../third_party/rust/LICENSE.md");
const KERNEL_JS: &[u8] = include_bytes!("../client/kernel.js");
const LINT_JS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.js");
const LINT_CSS: &[u8] = include_bytes!("../third_party/CodeMirror/addons/lint/lint.css");
const LINT_LICENSE: &[u8] = include_bytes!("../third_party/CodeMirror/LICENSE");
const VERSION_TXT: &[u8] = include_bytes!("../client/version.txt");

pub(crate) fn install() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    fs::create_dir_all(&kernel_dir)?;
    let current_exe_path = env::current_exe()?;
    let current_exe = current_exe_path
        .to_str()
        .ok_or_else(|| anyhow!("current exe path isn't valid UTF-8"))?;
    let kernel_json = object! {
        "argv" => array![current_exe, "--control_file", "{connection_file}"],
        "display_name" => "Rust",
        "language" => "rust",
        "interrupt_mode" => "message",
    };
    let kernel_json_filename = kernel_dir.join("kernel.json");
    println!("Writing {}", kernel_json_filename.to_string_lossy());
    kernel_json.write_pretty(&mut fs::File::create(kernel_json_filename)?, 2)?;
    install_resource(&kernel_dir, "logo-32x32.png", LOGO_32X32)?;
    install_resource(&kernel_dir, "logo-64x64.png", LOGO_64X64)?;
    install_resource(&kernel_dir, "logo-LICENSE.md", LOGO_LICENSE)?;
    install_resource(&kernel_dir, "kernel.js", KERNEL_JS)?;
    install_resource(&kernel_dir, "lint.js", LINT_JS)?;
    install_resource(&kernel_dir, "lint.css", LINT_CSS)?;
    install_resource(&kernel_dir, "lint-LICENSE", LINT_LICENSE)?;
    install_resource(&kernel_dir, "version.txt", VERSION_TXT)?;
    println!("Installation complete");
    Ok(())
}

/// Checks if the current installation is out-of-date, by looking at what's in
/// version.txt. If it is out of date, then updates it.
pub(crate) fn update_if_necessary() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    let installed_version = std::fs::read(kernel_dir.join("version.txt")).unwrap_or_default();
    if installed_version != VERSION_TXT {
        install()?;
        eprintln!(
            "\n\n==================================================================\n\
            Updated Evcxr Jupyter installation. Note, updates unfortunately \n\
            won't take effect until the next time you start jupyter notebook.\n\
            ==================================================================\n"
        );
    }
    Ok(())
}

pub(crate) fn install_resource(dir: &PathBuf, filename: &str, bytes: &'static [u8]) -> Result<()> {
    let res_path = dir.join(filename);
    println!("Writing {}", res_path.to_string_lossy());
    let mut file = fs::File::create(res_path)?;
    file.write_all(bytes)?;
    Ok(())
}

pub(crate) fn uninstall() -> Result<()> {
    let kernel_dir = get_kernel_dir()?;
    println!("Deleting {}", kernel_dir.to_string_lossy());
    fs::remove_dir_all(kernel_dir)?;
    println!("Uninstall complete");
    Ok(())
}

// https://jupyter-client.readthedocs.io/en/latest/kernels.html
fn get_kernel_dir() -> Result<PathBuf> {
    let jupyter_dir = if let Ok(dir) = env::var("JUPYTER_PATH") {
        PathBuf::from(dir)
    } else if let Some(dir) = get_user_kernel_dir() {
        dir
    } else {
        bail!("Couldn't get XDG data directory");
    };
    Ok(jupyter_dir.join("kernels").join("rust"))
}

#[cfg(not(target_os = "macos"))]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().map(|data_dir| data_dir.join("jupyter"))
}

#[cfg(target_os = "macos")]
fn get_user_kernel_dir() -> Option<PathBuf> {
    dirs::data_dir().and_then(|d| d.parent().map(|data_dir| data_dir.join("Jupyter")))
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! def_store_hash_map {
    ($m: ident <$kt: ty, $vt: ty>) => {
        pub mod $m {
            use $crate::raft::state_machine::StateMachineCtl;
            use $crate::raft::state_machine::callback::server::SMCallback;
            use $crate::raft::RaftService;
            use bifrost_hasher::hash_str;
            use std::collections::HashMap;
            use std::sync::{Arc};
            pub struct Map {
                map: HashMap<$kt, $vt>,
                callback: Option<SMCallback>,
                pub id: u64
            }
            raft_state_machine! {
                def qry get(k: $kt) -> Option<$vt>;
                def cmd insert(k: $kt, v: $vt) -> Option<$vt>;
                def cmd insert_if_absent(k: $kt, v: $vt) -> $vt;
                def cmd remove(k: $kt) -> Option<$vt>;

                def qry is_empty() -> bool;
                def qry len() -> u64;
                def cmd clear();

                def qry keys() -> Vec<$kt>;
                def qry values() -> Vec<$vt>;
                def qry entries() -> Vec<($kt, $vt)>;
                def qry clone() -> HashMap<$kt, $vt>;

                def qry contains_key(k: $kt) -> bool;

                def sub on_inserted() -> ($kt, $vt);
                def sub on_key_inserted(k: $kt) -> $vt;
                def sub on_removed() -> ($kt, $vt);
                def sub on_key_removed(k: $kt) -> $vt;
            }
            impl StateMachineCmds for Map {
                fn get(&self, k: $kt) -> Result<Option<$vt>, ()> {
                    Ok(
                        if let Some(v) = self.map.get(&k) {
                            Some(v.clone())
                        } else {None}
                    )
                }
                fn insert(&mut self, k: $kt, v: $vt) -> Result<Option<$vt>, ()> {
                    if let Some(ref callback) = self.callback {
                        callback.notify(&commands::on_inserted::new(), Ok((k.clone(), v.clone())));
                        callback.notify(&commands::on_key_inserted::new(&k), Ok(v.clone()));
                    }
                    Ok(self.map.insert(k, v))
                }
                fn insert_if_absent(&mut self, k: $kt, v: $vt) -> Result<$vt, ()> {
                    if let Some(v) = self.map.get(&k) {
                        return Ok(v.clone())
                    }
                    self.insert(k, v.clone()); Ok(v)
                }
                fn remove(&mut self, k: $kt) -> Result<Option<$vt>, ()> {
                    let res = self.map.remove(&k);
                    if let Some(ref callback) = self.callback {
                        if let Some(ref v) = res {
                            callback.notify(&commands::on_removed::new(), Ok((k.clone(), v.clone())));
                            callback.notify(&commands::on_key_removed::new(&k), Ok(v.clone()));
                        }
                    }
                    Ok(res)
                }
                fn is_empty(&self) -> Result<bool, ()> {
                    Ok(self.map.is_empty())
                }
                fn len(&self) -> Result<u64, ()> {
                    Ok(self.map.len() as u64)
                }
                fn clear(&mut self) -> Result<(), ()> {
                    Ok(self.map.clear())
                }
                fn keys(&self) -> Result<Vec<$kt>, ()> {
                    Ok(self.map.keys().cloned().collect())
                }
                fn values(&self) -> Result<Vec<$vt>, ()> {
                    Ok(self.map.values().cloned().collect())
                }
                fn entries(&self) -> Result<Vec<($kt, $vt)>, ()> {
                    let mut r = Vec::new();
                    for (k, v) in self.map.iter() {
                        r.push((k.clone(), v.clone()));
                    }
                    Ok(r)
                }
                fn clone(&self) -> Result<HashMap<$kt, $vt>, ()> {
                    Ok(self.map.clone())
                }
                fn contains_key(&self, k: $kt) -> Result<bool, ()> {
                    Ok(self.map.contains_key(&k))
                }
            }
            impl StateMachineCtl for Map {
                raft_sm_complete!();
                fn snapshot(&self) -> Option<Vec<u8>> {
                    Some($crate::utils::bincode::serialize(&self.map))
                }
                fn recover(&mut self, data: Vec<u8>) {
                    self.map = $crate::utils::bincode::deserialize(&data);
                }
                fn id(&self) -> u64 {self.id}
            }
            impl Map {
                pub fn new(id: u64) -> Map {
                    Map {
                        map: HashMap::new(),
                        callback: None,
                        id: id,
                    }
                }
                pub fn new_by_name(name: &String) -> Map {
                    Map::new(hash_str(name))
                }
                pub fn init_callback(&mut self, raft_service: &Arc<RaftService>) {
                    self.callback = Some(SMCallback::new(self.id(), raft_service.clone()));
                }
            }
        }
    };
}

def_store_hash_map!(string_u8vec_hashmap <String, Vec<u8>>);
def_store_hash_map!(string_string_hashmap <String, String>);<commit_msg>use super<commit_after>#[macro_export]
macro_rules! def_store_hash_map {
    ($m: ident <$kt: ty, $vt: ty>) => {
        pub mod $m {
            use $crate::raft::state_machine::StateMachineCtl;
            use $crate::raft::state_machine::callback::server::SMCallback;
            use $crate::raft::RaftService;
            use bifrost_hasher::hash_str;
            use std::collections::HashMap;
            use std::sync::{Arc};
            use super::*;
            pub struct Map {
                map: HashMap<$kt, $vt>,
                callback: Option<SMCallback>,
                pub id: u64
            }
            raft_state_machine! {
                def qry get(k: $kt) -> Option<$vt>;
                def cmd insert(k: $kt, v: $vt) -> Option<$vt>;
                def cmd insert_if_absent(k: $kt, v: $vt) -> $vt;
                def cmd remove(k: $kt) -> Option<$vt>;

                def qry is_empty() -> bool;
                def qry len() -> u64;
                def cmd clear();

                def qry keys() -> Vec<$kt>;
                def qry values() -> Vec<$vt>;
                def qry entries() -> Vec<($kt, $vt)>;
                def qry clone() -> HashMap<$kt, $vt>;

                def qry contains_key(k: $kt) -> bool;

                def sub on_inserted() -> ($kt, $vt);
                def sub on_key_inserted(k: $kt) -> $vt;
                def sub on_removed() -> ($kt, $vt);
                def sub on_key_removed(k: $kt) -> $vt;
            }
            impl StateMachineCmds for Map {
                fn get(&self, k: $kt) -> Result<Option<$vt>, ()> {
                    Ok(
                        if let Some(v) = self.map.get(&k) {
                            Some(v.clone())
                        } else {None}
                    )
                }
                fn insert(&mut self, k: $kt, v: $vt) -> Result<Option<$vt>, ()> {
                    if let Some(ref callback) = self.callback {
                        callback.notify(&commands::on_inserted::new(), Ok((k.clone(), v.clone())));
                        callback.notify(&commands::on_key_inserted::new(&k), Ok(v.clone()));
                    }
                    Ok(self.map.insert(k, v))
                }
                fn insert_if_absent(&mut self, k: $kt, v: $vt) -> Result<$vt, ()> {
                    if let Some(v) = self.map.get(&k) {
                        return Ok(v.clone())
                    }
                    self.insert(k, v.clone()); Ok(v)
                }
                fn remove(&mut self, k: $kt) -> Result<Option<$vt>, ()> {
                    let res = self.map.remove(&k);
                    if let Some(ref callback) = self.callback {
                        if let Some(ref v) = res {
                            callback.notify(&commands::on_removed::new(), Ok((k.clone(), v.clone())));
                            callback.notify(&commands::on_key_removed::new(&k), Ok(v.clone()));
                        }
                    }
                    Ok(res)
                }
                fn is_empty(&self) -> Result<bool, ()> {
                    Ok(self.map.is_empty())
                }
                fn len(&self) -> Result<u64, ()> {
                    Ok(self.map.len() as u64)
                }
                fn clear(&mut self) -> Result<(), ()> {
                    Ok(self.map.clear())
                }
                fn keys(&self) -> Result<Vec<$kt>, ()> {
                    Ok(self.map.keys().cloned().collect())
                }
                fn values(&self) -> Result<Vec<$vt>, ()> {
                    Ok(self.map.values().cloned().collect())
                }
                fn entries(&self) -> Result<Vec<($kt, $vt)>, ()> {
                    let mut r = Vec::new();
                    for (k, v) in self.map.iter() {
                        r.push((k.clone(), v.clone()));
                    }
                    Ok(r)
                }
                fn clone(&self) -> Result<HashMap<$kt, $vt>, ()> {
                    Ok(self.map.clone())
                }
                fn contains_key(&self, k: $kt) -> Result<bool, ()> {
                    Ok(self.map.contains_key(&k))
                }
            }
            impl StateMachineCtl for Map {
                raft_sm_complete!();
                fn snapshot(&self) -> Option<Vec<u8>> {
                    Some($crate::utils::bincode::serialize(&self.map))
                }
                fn recover(&mut self, data: Vec<u8>) {
                    self.map = $crate::utils::bincode::deserialize(&data);
                }
                fn id(&self) -> u64 {self.id}
            }
            impl Map {
                pub fn new(id: u64) -> Map {
                    Map {
                        map: HashMap::new(),
                        callback: None,
                        id: id,
                    }
                }
                pub fn new_by_name(name: &String) -> Map {
                    Map::new(hash_str(name))
                }
                pub fn init_callback(&mut self, raft_service: &Arc<RaftService>) {
                    self.callback = Some(SMCallback::new(self.id(), raft_service.clone()));
                }
            }
        }
    };
}

def_store_hash_map!(string_u8vec_hashmap <String, Vec<u8>>);
def_store_hash_map!(string_string_hashmap <String, String>);<|endoftext|>


<commit_before>use time;
use chrono::prelude::Local;
use ansi_term::Colour;
use ansi_term::Colour::{Green, Red, Yellow};
use logger::Logger;

#[derive(PartialEq, Clone)]
pub enum TestCaseStatus {
    PASSED,
    FAILED,
    SKIPPED,
    UNKNOWN,
}

pub struct TestCase {
    pub title: &'static str,
    pub criteria: &'static str,
    pub exec: Box<Fn(&mut Logger) -> TestCaseStatus>,
}
impl TestCase {
    pub fn new(title: &'static str,
               criteria: &'static str,
               exec: Box<Fn(&mut Logger) -> TestCaseStatus>)
               -> TestCase {
        TestCase {
            title: title,
            criteria: criteria,
            exec: exec,
        }
    }
}

pub struct TestCaseResults {
    title: &'static str,
    criteria: &'static str,
    duration: i32,
    status: TestCaseStatus,
}

pub trait Testable {
    fn tests(self) -> Vec<TestCase>;
}

#[allow(dead_code)]
pub struct TestRunnerAttributes {
    pub bail_out_after_first_failure: i64,
    pub disable_final_stats:          i64,
    pub minimize_output:              i64,
    // more attributes can be added here
}

// attribute values need to be assigned here if new attributes are added to TestRunnerAttributes
pub static TEST_RUNNER_ATTRIBUTES: TestRunnerAttributes = TestRunnerAttributes {
    bail_out_after_first_failure: 0x1,
    disable_final_stats:          0x2,
    minimize_output:              0x4,
};

#[allow(dead_code)]
pub struct TestRunner {
    attributes: i64,
    results: Vec<TestCaseResults>,
    module_path: &'static str,
}
impl TestRunner {
    pub fn new() -> TestRunner {
        TestRunner {
            attributes: 0,
            results: vec![],
            module_path: "",
        }
    }
    pub fn set_module_path(&mut self, path: &'static str) -> &mut Self {
        self.module_path = path;
        self
    }
    pub fn set_attribute(&mut self, attribute: i64) -> &mut Self {
        self.attributes |= attribute;
        self
    }
    pub fn set_attributes(&mut self, attributes: i64) -> &mut Self {
        self.attributes = attributes;
        self
    }
    pub fn has_attribute(&self, attribute: i64) -> bool {
        self.attributes & attribute == attribute
    }
    pub fn run_test(&mut self, test: TestCase) -> bool {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Starting {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
        }
        let mut logger: Logger = Logger::new();
        let starting_time: i32 = time::now().tm_nsec;
        let mut status: TestCaseStatus = (test.exec)(&mut logger);
        let ending_time: i32 = time::now().tm_nsec;
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Ended {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
            logger.drop();
        }
        if status == TestCaseStatus::UNKNOWN {
            if logger.get_num_fail() > 0 {
                status = TestCaseStatus::FAILED;
            } else {
                status = TestCaseStatus::PASSED;
            }
        }
        let mark = match status {
            TestCaseStatus::PASSED => "✅",
            TestCaseStatus::FAILED => "❌",
            TestCaseStatus::SKIPPED => "❗",
            TestCaseStatus::UNKNOWN => "",
        };
        let formatted_criteria = match status {
            TestCaseStatus::PASSED => Green.paint(test.criteria),
            TestCaseStatus::FAILED => Red.paint(test.criteria),
            TestCaseStatus::SKIPPED => Yellow.paint(test.criteria),
            TestCaseStatus::UNKNOWN => Yellow.paint(test.criteria),
        };
        if self.module_path.len() > 0 {
            println!("{} {}: {}", mark, format!("{}::{}", self.module_path, test.title), formatted_criteria);
        }
        else {
            println!("{} {}: {}", mark, test.title, formatted_criteria);
        }
        let test_info = TestCaseResults {
            title: test.title,
            criteria: test.criteria,
            duration: (ending_time - starting_time) / 1000,
            status: status.clone(),
        };
        self.results.push(test_info);
        return status == TestCaseStatus::PASSED;
    }
    pub fn run_tests(&mut self, tests: Vec<TestCase>) -> bool {
        for test in tests {
            if !self.run_test(test) && self.has_attribute(TEST_RUNNER_ATTRIBUTES.bail_out_after_first_failure) {
                return false;
            }
        }
        return true;
    }
    pub fn run_tests_from_class<T: Testable>(&mut self, test_class: T) -> bool {
        return self.run_tests(test_class.tests());
    }
}
impl Drop for TestRunner {
    fn drop(&mut self) {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.disable_final_stats) {
            let (mut total_count, mut total_duration): (i32, i32) = (0, 0);
            let (mut pass, mut fail, mut skip): (i32, i32, i32) = (0, 0, 0);
            print!("\n");
            for stat in self.results.iter() {
                let color: Colour;
                match stat.status {
                    TestCaseStatus::PASSED => {
                        pass += 1;
                        color = Green;
                    },
                    TestCaseStatus::FAILED => {
                        fail += 1;
                        color = Red;
                    },
                    TestCaseStatus::SKIPPED => {
                        skip += 1;
                        color = Yellow;
                    },
                    _ => {
                        color = Yellow;
                    }
                };
                total_count += 1;
                total_duration += stat.duration;
                let formatted_text = color.paint(format!("{} ({}) ... {}ns",
                                                         stat.title,
                                                         stat.criteria,
                                                         stat.duration));
                println!("{}", formatted_text);
            }
            println!("\nRan {} test(s) in {}ns", total_count, total_duration);
            let formatted_pass = Green.paint(format!("{} Passed", pass));
            let formatted_failed = Red.paint(format!("{} Failed", fail));
            let formatted_skipped = Yellow.paint(format!("{} Skipped", skip));
            println!("{}  {}  {}",
                     formatted_pass,
                     formatted_failed,
                     formatted_skipped);
        }
    }
}
<commit_msg>satisfies #27<commit_after>use time;
use chrono::prelude::Local;
use ansi_term::Colour;
use ansi_term::Colour::{Green, Red, Yellow};
use logger::Logger;

#[derive(PartialEq, Clone)]
pub enum TestCaseStatus {
    PASSED,
    FAILED,
    SKIPPED,
    UNKNOWN,
}

pub struct TestCase {
    pub title: &'static str,
    pub criteria: &'static str,
    pub exec: Box<Fn(&mut Logger) -> TestCaseStatus>,
}
impl TestCase {
    pub fn new(title: &'static str,
               criteria: &'static str,
               exec: Box<Fn(&mut Logger) -> TestCaseStatus>)
               -> TestCase {
        TestCase {
            title: title,
            criteria: criteria,
            exec: exec,
        }
    }
}

pub struct TestCaseResults {
    title: &'static str,
    criteria: &'static str,
    duration: i32,
    status: TestCaseStatus,
}

pub trait Testable {
    fn tests(self) -> Vec<TestCase>;
}

#[allow(dead_code)]
pub struct TestRunnerAttributes {
    pub bail_out_after_first_failure: i64,
    pub disable_final_stats:          i64,
    pub minimize_output:              i64,
    // more attributes can be added here
}

// attribute values need to be assigned here if new attributes are added to TestRunnerAttributes
pub static TEST_RUNNER_ATTRIBUTES: TestRunnerAttributes = TestRunnerAttributes {
    bail_out_after_first_failure: 0x1,
    disable_final_stats:          0x2,
    minimize_output:              0x4,
};

#[allow(dead_code)]
pub struct TestRunner {
    attributes: i64,
    results: Vec<TestCaseResults>,
    module_path: &'static str,
}
impl TestRunner {
    pub fn new() -> TestRunner {
        TestRunner {
            attributes: 0,
            results: vec![],
            module_path: "",
        }
    }
    pub fn set_module_path(&mut self, path: &'static str) -> &mut Self {
        self.module_path = path;
        self
    }
    pub fn set_attribute(&mut self, attribute: i64) -> &mut Self {
        self.attributes |= attribute;
        self
    }
    pub fn set_attributes(&mut self, attributes: i64) -> &mut Self {
        self.attributes = attributes;
        self
    }
    pub fn has_attribute(&self, attribute: i64) -> bool {
        self.attributes & attribute == attribute
    }
    pub fn run_test(&mut self, test: TestCase) -> bool {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Starting {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
        }
        let mut logger: Logger = Logger::new();
        let starting_time: i32 = time::now().tm_nsec;
        let mut status: TestCaseStatus = (test.exec)(&mut logger);
        let ending_time: i32 = time::now().tm_nsec;
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.minimize_output) {
            println!("Ended {} at {} on {}",
                     test.title,
                     Local::now().format("%H:%M:%S").to_string(),
                     Local::now().format("%Y-%m-%d").to_string());
            logger.drop();
        }
        if status == TestCaseStatus::UNKNOWN {
            if logger.get_num_fail() > 0 {
                status = TestCaseStatus::FAILED;
            } else {
                status = TestCaseStatus::PASSED;
            }
        }
        let mark = match status {
            TestCaseStatus::PASSED => "✅",
            TestCaseStatus::FAILED => "❌",
            TestCaseStatus::SKIPPED => "❗",
            TestCaseStatus::UNKNOWN => "",
        };
        let formatted_criteria = match status {
            TestCaseStatus::PASSED => Green.paint(test.criteria),
            TestCaseStatus::FAILED => Red.paint(test.criteria),
            TestCaseStatus::SKIPPED => Yellow.paint(test.criteria),
            TestCaseStatus::UNKNOWN => Yellow.paint(test.criteria),
        };
        let test_info = TestCaseResults {
            title: test.title,
            criteria: test.criteria,
            duration: (ending_time - starting_time) / 1000,
            status: status.clone(),
        };
        if self.module_path.len() > 0 {
            println!("{} {}::{}: {} ({}ns)", mark, self.module_path, test.title, formatted_criteria, test_info.duration);
        }
        else {
            println!("{} {}: {} ({}ns)", mark, test.title, formatted_criteria, test_info.duration);
        }
        self.results.push(test_info);
        return status == TestCaseStatus::PASSED;
    }
    pub fn run_tests(&mut self, tests: Vec<TestCase>) -> bool {
        for test in tests {
            if !self.run_test(test) && self.has_attribute(TEST_RUNNER_ATTRIBUTES.bail_out_after_first_failure) {
                return false;
            }
        }
        return true;
    }
    pub fn run_tests_from_class<T: Testable>(&mut self, test_class: T) -> bool {
        return self.run_tests(test_class.tests());
    }
}
impl Drop for TestRunner {
    fn drop(&mut self) {
        if !self.has_attribute(TEST_RUNNER_ATTRIBUTES.disable_final_stats) {
            let (mut total_count, mut total_duration): (i32, i32) = (0, 0);
            let (mut pass, mut fail, mut skip): (i32, i32, i32) = (0, 0, 0);
            print!("\n");
            for stat in self.results.iter() {
                let color: Colour;
                match stat.status {
                    TestCaseStatus::PASSED => {
                        pass += 1;
                        color = Green;
                    },
                    TestCaseStatus::FAILED => {
                        fail += 1;
                        color = Red;
                    },
                    TestCaseStatus::SKIPPED => {
                        skip += 1;
                        color = Yellow;
                    },
                    _ => {
                        color = Yellow;
                    }
                };
                total_count += 1;
                total_duration += stat.duration;
                let formatted_text = color.paint(format!("{} ({}) ... {}ns",
                                                         stat.title,
                                                         stat.criteria,
                                                         stat.duration));
                println!("{}", formatted_text);
            }
            println!("\nRan {} test(s) in {}ns", total_count, total_duration);
            let formatted_pass = Green.paint(format!("{} Passed", pass));
            let formatted_failed = Red.paint(format!("{} Failed", fail));
            let formatted_skipped = Yellow.paint(format!("{} Skipped", skip));
            println!("{}  {}  {}",
                     formatted_pass,
                     formatted_failed,
                     formatted_skipped);
        }
    }
}
<|endoftext|>


<commit_before>use encoding::EncodingRef;
use encoding::all::UTF_8;
use loirc::{MonitorSettings, ReconnectionSettings};

/// Settings for the dispatcher.
pub struct Settings<'a> {
    /// Address of the irc server.
    pub addr: &'a str,
    /// Preferred nickname.
    pub nickname: &'a str,
    /// Username.
    pub username: &'a str,
    /// Real name.
    pub realname: &'a str,
    /// Reconnection settings. If None, reconnection is disabled.
    pub reconnection: ReconnectionSettings,
    /// Monitor settings. If None, monitoring is disabled.
    pub monitor: Option<MonitorSettings>,
    /// Automatically identify after reconnection.
    pub auto_ident: bool,
    /// Automatically reply to ping requests.
    pub auto_ping: bool,
    /// Encoding used for the connection.
    pub encoding: EncodingRef,
}

impl<'a> Settings<'a> {

    /// Create new settings with sensible default values.
    ///
    /// The default values are:
    ///
    /// ```
    /// username: "hiirc",
    /// realname: "hiirc",
    /// reconnection: ReonnectionSettings::DoNotReconnect,
    /// monitor: None,
    /// auto_ident: true,
    /// auto_ping: true,
    /// encoding: UTF_8,
    /// ```
    pub fn new<'b>(addr: &'b str, nickname: &'b str) -> Settings<'b> {
        Settings {
            addr: addr,
            nickname: nickname,
            username: "hiirc",
            realname: "hiirc",
            reconnection: ReconnectionSettings::DoNotReconnect,
            monitor: None,
            auto_ident: true,
            auto_ping: true,
            encoding: UTF_8,
        }
    }

}
<commit_msg>Fix an issue with doc tests<commit_after>use encoding::EncodingRef;
use encoding::all::UTF_8;
use loirc::{MonitorSettings, ReconnectionSettings};

/// Settings for the dispatcher.
pub struct Settings<'a> {
    /// Address of the irc server.
    pub addr: &'a str,
    /// Preferred nickname.
    pub nickname: &'a str,
    /// Username.
    pub username: &'a str,
    /// Real name.
    pub realname: &'a str,
    /// Reconnection settings. If None, reconnection is disabled.
    pub reconnection: ReconnectionSettings,
    /// Monitor settings. If None, monitoring is disabled.
    pub monitor: Option<MonitorSettings>,
    /// Automatically identify after reconnection.
    pub auto_ident: bool,
    /// Automatically reply to ping requests.
    pub auto_ping: bool,
    /// Encoding used for the connection.
    pub encoding: EncodingRef,
}

impl<'a> Settings<'a> {

    /// Create new settings with sensible default values.
    ///
    /// The default values are:
    ///
    /// ```ignore
    /// username: "hiirc",
    /// realname: "hiirc",
    /// reconnection: ReonnectionSettings::DoNotReconnect,
    /// monitor: None,
    /// auto_ident: true,
    /// auto_ping: true,
    /// encoding: UTF_8,
    /// ```
    pub fn new<'b>(addr: &'b str, nickname: &'b str) -> Settings<'b> {
        Settings {
            addr: addr,
            nickname: nickname,
            username: "hiirc",
            realname: "hiirc",
            reconnection: ReconnectionSettings::DoNotReconnect,
            monitor: None,
            auto_ident: true,
            auto_ping: true,
            encoding: UTF_8,
        }
    }

}
<|endoftext|>