<commit_before>extern crate rs6502;
extern crate sdl2;

mod console;
mod position;
mod text;

use std::io::{self, BufRead, Write};

use rs6502::{CodeSegment, Cpu, Disassembler};
use sdl2::render::Renderer;

pub use self::console::Console;
pub use self::position::Position;
pub use self::text::Text;

const HELPTEXT: &'static str = "

HAKKA
-----

Commands:

break | b: addr
       - Toggles a breakpoint at a specific address. If the
         program counter hits this address, execution
         stops.

step | s
       - Executes the current instruction before breaking
         execution again.

continue | c
       - Resumes execution of code within the virtual machine.

list
       - Lists the code surrounding the current program
         counter.

memset | set: addr args [args, ...]
       - memset sets the value of memory directly.

memdmp | dmp: page || start end
       - memdmp dumps a single memory page, or a specified
         memory range from <start> to <end> (inclusive).

monitor | mon: start end
       - monitor dumps the memory between start and end (inclusive)
         every second. Press ENTER to stop the monitor.

source
       - Lists the code currently running in the virtual
         machine. A '>' symbol indicates the current
         program counter.

registers | reg
       - Lists the CPU registers and their current values.

help
       - Lists this help text.
";

#[derive(Debug)]
pub struct MemoryMonitor {
    pub enabled: bool,
    start_addr: usize,
    end_addr: usize,
}

pub struct VirtualMachine<'a> {
    pub cpu: Cpu,
    pub monitor: MemoryMonitor,
    pub console: Console<'a>,
    segments: Vec<CodeSegment>,
    clock_rate: Option<u32>,
    last_command: String,
    breakpoints: [u8; 64 * 1024],
    broken: bool,
    step: bool,
}

impl<'a> VirtualMachine<'a> {
    pub fn new<CR>(cpu: Cpu, clock_rate: CR, mut console: Console<'a>) -> VirtualMachine<'a>
        where CR: Into<Option<u32>>
    {
        console.println("Welcome to hakka. Type 'help' for instructions.");
        console.println("");

        VirtualMachine {
            cpu: cpu,
            console: console,
            segments: Vec::new(),
            clock_rate: clock_rate.into(),
            monitor: MemoryMonitor {
                enabled: false,
                start_addr: 0,
                end_addr: 0,
            },
            last_command: "".into(),
            breakpoints: [0; 64 * 1024],
            broken: false,
            step: false,
        }
    }

    pub fn render(&mut self, mut renderer: &mut Renderer) {
        self.console.render(renderer);
    }

    pub fn load_code_segments(&mut self, segments: Vec<CodeSegment>) {
        if segments.len() == 0 {
            return;
        }
        self.segments = segments;
        for segment in &self.segments {
            self.cpu.load(&segment.code, segment.address);
        }

        self.cpu.registers.PC = self.segments[0].address;
    }

    /// Cycles the Virtual Machine CPU according to the clock rate
    pub fn cycle(&mut self) {
        if let Some(clock_rate) = self.clock_rate {
            let mut n = 0;
            while (n < clock_rate && !self.broken) || self.step {
                n += self.cpu.step().expect("SEGFAULT") as u32;
                if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                    self.broken = true;
                    self.console.println("");
                    self.console
                        .println(format!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC));
                    self.console.toggle();
                }
                // If we stepped, dump the local disassembly
                if self.step {
                    self.dump_local_disassembly();
                }
                self.step = false;
            }
        } else {
            self.cpu.step().expect("SEGFAULT");
            if self.step {
                self.dump_local_disassembly();
            }
            self.step = false;
            if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                self.broken = true;
                self.console.println("");
                self.console.println(format!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC));
                self.console.toggle();
            }
        }
    }

    pub fn execute_command<S>(&mut self, cmd: S)
        where S: Into<String>
    {
        let mut input: String = cmd.into().trim().into();

        if input == "r" || input == "repeat" {
            input = self.last_command.clone();
        }

        let parts = input.split(" ").collect::<Vec<_>>();

        if input.len() == 0 {
            if self.monitor.enabled {
                self.monitor.enabled = false;
            }
        } else if parts[0] == "clear" || parts[0] == "cls" {
            self.console.clear();
        } else if parts[0] == "help" {
            self.console.print_lines(format!("{}", HELPTEXT));
        } else if parts[0] == "source" {
            self.dump_disassembly();
        } else if parts[0] == "list" {
            self.dump_local_disassembly();
        } else if parts[0] == "monitor" || parts[0] == "mon" {
            if self.monitor.enabled {
                self.monitor.enabled = false;
            } else {
                self.enable_memory_monitor(&input);
            }
        } else if parts[0] == "memset" || parts[0] == "set" {
            if parts.len() < 3 {
                self.console
                    .println("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
            } else if parts.len() == 3 {
                if let Ok(dst) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    if let Ok(src) = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                        self.cpu.memory[dst as usize] = src;
                    } else {
                        self.console.println("ERR: Unable to parse source byte value");
                    }
                } else {
                    self.console.println("ERR: Unable to parse destination byte value");
                }
            } else {
                if let Ok(mut dst) = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    for p in &parts[2..] {
                        if let Ok(byte) = u8::from_str_radix(&p.replace("0x", "")[..], 16) {
                            self.cpu.memory[dst] = byte;
                            dst += 0x01;
                        } else {
                            self.console.println("ERR: Unable to parse source byte value");
                        }
                    }
                } else {
                    self.console.println("ERR: Unable to parse destination byte value");
                }
            }
        } else if parts[0] == "memdmp" || parts[0] == "dmp" {
            // 1 argument assumes 1 memory "page"
            if parts.len() == 2 {
                if let Ok(page_number) = parts[1].parse() {

                    self.dump_memory_page(page_number);
                } else {
                    self.console.println("ERR: Unable to parse memory page");
                }
            } else if parts.len() == 3 {
                // A memory range instead
                if let Ok(start) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    if let Ok(end) = u16::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                        self.dump_memory_range(start, end);
                    } else {
                        self.console.println("ERR: Unable to parse end address value");
                    }
                } else {
                    self.console.println("ERR: Unable to parse start address value");
                }
            }
        } else if parts[0] == "registers" || parts[0] == "reg" {
            self.dump_registers();
        } else if parts[0] == "break" || parts[0] == "b" {
            // 1 argument assumes 1 memory "page"
            if parts.len() == 2 {
                if let Ok(addr) = usize::from_str_radix(&parts[1][..], 16) {
                    if addr <= u16::max_value() as usize {
                        if self.breakpoints[addr] > 0 {
                            self.console.println(format!("Removed breakpoint at {:04X}", addr));
                            self.breakpoints[addr] = 0;
                        } else {
                            self.console.println(format!("Added breakpoint at {:04X}", addr));
                            self.breakpoints[addr] = 1;
                        }
                    } else {
                        self.console.println("ERR: Value outside addressable range.");
                    }
                } else {
                    self.broken = true;
                    println!("Execution stopped");
                }
            } else {
                self.console.println("ERR: Requires 1 argument");
            }
        } else if parts[0] == "continue" || parts[0] == "c" {
            self.broken = false;
            self.console.println("Execution resumed");
        } else if parts[0] == "step" || parts[0] == "s" {
            self.broken = true;
            self.step = true;
        } else {
            self.console.println("Unknown command");
        }

        // Don't assign a blank command as a last command
        if input.len() > 0 {
            self.last_command = input.clone();
        }
    }

    fn enable_memory_monitor(&mut self, input: &str) {
        let parts: Vec<&str> = input.split(" ").collect();
        if parts.len() < 3 {
            self.console.println("ERR: Requires 2 arguments. Example: monitor 0x00 0xFF");
        } else {
            let start = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
            let end = usize::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

            self.monitor.start_addr = start;
            self.monitor.end_addr = end;
            self.monitor.enabled = true;
        }
    }

    fn dump_disassembly(&mut self) {
        self.console.println("");

        for segment in &self.segments {
            self.console.println(format!(".ORG ${:04X}", segment.address));
            let disassembler = Disassembler::with_offset(segment.address);
            let pairs = disassembler.disassemble_with_addresses(&segment.code);
            for line in self.highlight_lines(self.cpu.registers.PC as usize,
                                             pairs,
                                             segment.address,
                                             false) {
                self.console.println(format!("{}", line));
            }
            self.console.println("");
        }
    }

    fn dump_local_disassembly(&mut self) {
        self.console.println("");

        let result = {
            let pc = self.cpu.registers.PC as usize;
            let local_segment = self.get_local_segment(pc);
            let disassembler = Disassembler::with_offset(local_segment.address);
            let pairs = disassembler.disassemble_with_addresses(&local_segment.code);
            self.highlight_lines(pc, pairs, local_segment.address, true)
        };
        for line in result {
            self.console.println(format!("{}", line));
        }
        self.console.println("");
    }

    pub fn dump_memory_page(&mut self, page: usize) {
        let mut addr = page * 0x100;
        for chunk in self.cpu.memory[page * 0x100..(page * 0x100) + 0x100].chunks(8) {
            self.console.print(format!("{:04X}: ", addr));
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.wrap_line();
            addr += 0x08;
        }
    }

    pub fn dump_memory(&mut self) {
        self.console.println("");
        for chunk in self.cpu.memory[self.monitor.start_addr..self.monitor.end_addr + 0x01]
            .chunks(8) {
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.println("");
        }
    }

    fn dump_memory_range(&mut self, start: u16, end: u16) {
        self.console.println("");
        let start = start as usize;
        let end = end as usize;
        for chunk in self.cpu.memory[start..end + 0x01].chunks(8) {
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.println("");
        }
    }

    fn dump_registers(&mut self) {
        self.console.println("");
        self.console.println(format!("A: {} ({:04X})", self.cpu.registers.A, self.cpu.registers.A));
        self.console.println(format!("X: {} ({:04X})", self.cpu.registers.X, self.cpu.registers.X));
        self.console.println(format!("Y: {} ({:04X})", self.cpu.registers.Y, self.cpu.registers.Y));
        self.console.println(format!("PC: {} ({:04X})",
                                     self.cpu.registers.PC,
                                     self.cpu.registers.PC));
        self.console.println(format!("S: {} ({:04X})",
                                     self.cpu.stack.pointer,
                                     self.cpu.stack.pointer));
    }

    fn get_local_segment(&self, pc: usize) -> &CodeSegment {
        for segment in &self.segments {
            let addr = segment.address as usize;
            if pc >= addr && pc <= addr + segment.code.len() {
                return segment;
            }
        }

        &self.segments[0]
    }

    fn highlight_lines(&self,
                       pc: usize,
                       pairs: Vec<(String, u16)>,
                       segment_start: u16,
                       limit_results: bool)
                       -> Vec<String> {
        let mut result = Vec::new();

        let base = pc as isize - segment_start as isize;

        for pair in pairs {
            if limit_results {
                let start = if base > 0x0A { base - 0x0A } else { 0 };
                if (pair.1 as isize) < start || (pair.1 as isize) > base + 0x0A {
                    continue;
                }
            }
            let current_line = pc as u16 == segment_start + pair.1;
            let breakpoint = self.breakpoints[segment_start as usize + pair.1 as usize] > 0x00;

            if breakpoint && current_line {
                result.push(format!("> * {}", pair.0));
            } else if breakpoint && !current_line {
                result.push(format!("  * {}", pair.0));
            } else if !breakpoint && current_line {
                result.push(format!(">   {}", pair.0));
            } else {
                result.push(format!("    {}", pair.0));
            }
        }

        result
    }
}
<commit_msg>Improve disassembly output logic<commit_after>extern crate rs6502;
extern crate sdl2;

mod console;
mod position;
mod text;

use std::io::{self, BufRead, Write};

use rs6502::{CodeSegment, Cpu, Disassembler};
use sdl2::render::Renderer;

pub use self::console::Console;
pub use self::position::Position;
pub use self::text::Text;

const HELPTEXT: &'static str = "

HAKKA
-----

Commands:

break | b: addr
       - Toggles a breakpoint at a specific address. If the
         program counter hits this address, execution
         stops.

step | s
       - Executes the current instruction before breaking
         execution again.

continue | c
       - Resumes execution of code within the virtual machine.

list
       - Lists the code surrounding the current program
         counter.

memset | set: addr args [args, ...]
       - memset sets the value of memory directly.

memdmp | dmp: page || start end
       - memdmp dumps a single memory page, or a specified
         memory range from <start> to <end> (inclusive).

monitor | mon: start end
       - monitor dumps the memory between start and end (inclusive)
         every second. Press ENTER to stop the monitor.

source
       - Lists the code currently running in the virtual
         machine. A '>' symbol indicates the current
         program counter.

registers | reg
       - Lists the CPU registers and their current values.

help
       - Lists this help text.
";

#[derive(Debug)]
pub struct MemoryMonitor {
    pub enabled: bool,
    start_addr: usize,
    end_addr: usize,
}

pub struct VirtualMachine<'a> {
    pub cpu: Cpu,
    pub monitor: MemoryMonitor,
    pub console: Console<'a>,
    segments: Vec<CodeSegment>,
    clock_rate: Option<u32>,
    last_command: String,
    breakpoints: [u8; 64 * 1024],
    broken: bool,
    step: bool,
}

impl<'a> VirtualMachine<'a> {
    pub fn new<CR>(cpu: Cpu, clock_rate: CR, mut console: Console<'a>) -> VirtualMachine<'a>
        where CR: Into<Option<u32>>
    {
        console.println("Welcome to hakka. Type 'help' for instructions.");
        console.println("");

        VirtualMachine {
            cpu: cpu,
            console: console,
            segments: Vec::new(),
            clock_rate: clock_rate.into(),
            monitor: MemoryMonitor {
                enabled: false,
                start_addr: 0,
                end_addr: 0,
            },
            last_command: "".into(),
            breakpoints: [0; 64 * 1024],
            broken: false,
            step: false,
        }
    }

    pub fn render(&mut self, mut renderer: &mut Renderer) {
        self.console.render(renderer);
    }

    pub fn load_code_segments(&mut self, segments: Vec<CodeSegment>) {
        if segments.len() == 0 {
            return;
        }
        self.segments = segments;
        for segment in &self.segments {
            self.cpu.load(&segment.code, segment.address);
        }

        self.cpu.registers.PC = self.segments[0].address;
    }

    /// Cycles the Virtual Machine CPU according to the clock rate
    pub fn cycle(&mut self) {
        if let Some(clock_rate) = self.clock_rate {
            let mut n = 0;
            while (n < clock_rate && !self.broken) || self.step {
                n += self.cpu.step().expect("SEGFAULT") as u32;
                if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                    self.broken = true;
                    self.console.println("");
                    self.console
                        .println(format!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC));
                    self.console.toggle();
                }
                // If we stepped, dump the local disassembly
                if self.step {
                    self.dump_local_disassembly();
                }
                self.step = false;
            }
        } else {
            self.cpu.step().expect("SEGFAULT");
            if self.step {
                self.dump_local_disassembly();
            }
            self.step = false;
            if self.breakpoints[self.cpu.registers.PC as usize] > 0 {
                self.broken = true;
                self.console.println("");
                self.console.println(format!("BREAKPOINT hit at {:04X}", self.cpu.registers.PC));
                self.console.toggle();
            }
        }
    }

    pub fn execute_command<S>(&mut self, cmd: S)
        where S: Into<String>
    {
        let mut input: String = cmd.into().trim().into();

        if input == "r" || input == "repeat" {
            input = self.last_command.clone();
        }

        let parts = input.split(" ").collect::<Vec<_>>();

        if input.len() == 0 {
            if self.monitor.enabled {
                self.monitor.enabled = false;
            }
        } else if parts[0] == "clear" || parts[0] == "cls" {
            self.console.clear();
        } else if parts[0] == "help" {
            self.console.print_lines(format!("{}", HELPTEXT));
        } else if parts[0] == "source" {
            self.dump_disassembly();
        } else if parts[0] == "list" {
            self.dump_local_disassembly();
        } else if parts[0] == "monitor" || parts[0] == "mon" {
            if self.monitor.enabled {
                self.monitor.enabled = false;
            } else {
                self.enable_memory_monitor(&input);
            }
        } else if parts[0] == "memset" || parts[0] == "set" {
            if parts.len() < 3 {
                self.console
                    .println("ERR: Requires 2 arguments. Example: memset 0x00 0x01 to store 0x01 \
                              in 0x00.");
            } else if parts.len() == 3 {
                if let Ok(dst) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    if let Ok(src) = u8::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                        self.cpu.memory[dst as usize] = src;
                    } else {
                        self.console.println("ERR: Unable to parse source byte value");
                    }
                } else {
                    self.console.println("ERR: Unable to parse destination byte value");
                }
            } else {
                if let Ok(mut dst) = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    for p in &parts[2..] {
                        if let Ok(byte) = u8::from_str_radix(&p.replace("0x", "")[..], 16) {
                            self.cpu.memory[dst] = byte;
                            dst += 0x01;
                        } else {
                            self.console.println("ERR: Unable to parse source byte value");
                        }
                    }
                } else {
                    self.console.println("ERR: Unable to parse destination byte value");
                }
            }
        } else if parts[0] == "memdmp" || parts[0] == "dmp" {
            // 1 argument assumes 1 memory "page"
            if parts.len() == 2 {
                if let Ok(page_number) = parts[1].parse() {

                    self.dump_memory_page(page_number);
                } else {
                    self.console.println("ERR: Unable to parse memory page");
                }
            } else if parts.len() == 3 {
                // A memory range instead
                if let Ok(start) = u16::from_str_radix(&parts[1].replace("0x", "")[..], 16) {
                    if let Ok(end) = u16::from_str_radix(&parts[2].replace("0x", "")[..], 16) {
                        self.dump_memory_range(start, end);
                    } else {
                        self.console.println("ERR: Unable to parse end address value");
                    }
                } else {
                    self.console.println("ERR: Unable to parse start address value");
                }
            }
        } else if parts[0] == "registers" || parts[0] == "reg" {
            self.dump_registers();
        } else if parts[0] == "break" || parts[0] == "b" {
            // 1 argument assumes 1 memory "page"
            if parts.len() == 2 {
                if let Ok(addr) = usize::from_str_radix(&parts[1][..], 16) {
                    if addr <= u16::max_value() as usize {
                        if self.breakpoints[addr] > 0 {
                            self.console.println(format!("Removed breakpoint at {:04X}", addr));
                            self.breakpoints[addr] = 0;
                        } else {
                            self.console.println(format!("Added breakpoint at {:04X}", addr));
                            self.breakpoints[addr] = 1;
                        }
                    } else {
                        self.console.println("ERR: Value outside addressable range.");
                    }
                } else {
                    self.broken = true;
                    println!("Execution stopped");
                }
            } else {
                self.console.println("ERR: Requires 1 argument");
            }
        } else if parts[0] == "continue" || parts[0] == "c" {
            self.broken = false;
            self.console.println("Execution resumed");
        } else if parts[0] == "step" || parts[0] == "s" {
            self.broken = true;
            self.step = true;
        } else {
            self.console.println("Unknown command");
        }

        // Don't assign a blank command as a last command
        if input.len() > 0 {
            self.last_command = input.clone();
        }
    }

    fn enable_memory_monitor(&mut self, input: &str) {
        let parts: Vec<&str> = input.split(" ").collect();
        if parts.len() < 3 {
            self.console.println("ERR: Requires 2 arguments. Example: monitor 0x00 0xFF");
        } else {
            let start = usize::from_str_radix(&parts[1].replace("0x", "")[..], 16).unwrap();
            let end = usize::from_str_radix(&parts[2].replace("0x", "")[..], 16).unwrap();

            self.monitor.start_addr = start;
            self.monitor.end_addr = end;
            self.monitor.enabled = true;
        }
    }

    fn dump_disassembly(&mut self) {
        self.console.println("");

        for segment in &self.segments {
            self.console.println(format!(".ORG ${:04X}", segment.address));
            let disassembler = Disassembler::with_offset(segment.address);
            let pairs = disassembler.disassemble_with_addresses(&segment.code);
            let lines = self.highlight_lines(self.cpu.registers.PC as usize,
                                             pairs,
                                             segment.address,
                                             false);
            self.console.print_lines(lines.join(""));
        }
        self.console.println("");
    }

    fn dump_local_disassembly(&mut self) {
        self.console.println("");

        let result = {
            let pc = self.cpu.registers.PC as usize;
            let local_segment = self.get_local_segment(pc);
            let disassembler = Disassembler::with_offset(local_segment.address);
            let pairs = disassembler.disassemble_with_addresses(&local_segment.code);
            self.highlight_lines(pc, pairs, local_segment.address, true)
        };
        self.console.print_lines(result.join(""));
        self.console.println("");
    }

    pub fn dump_memory_page(&mut self, page: usize) {
        let mut addr = page * 0x100;
        for chunk in self.cpu.memory[page * 0x100..(page * 0x100) + 0x100].chunks(8) {
            self.console.print(format!("{:04X}: ", addr));
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.wrap_line();
            addr += 0x08;
        }
    }

    pub fn dump_memory(&mut self) {
        self.console.println("");
        for chunk in self.cpu.memory[self.monitor.start_addr..self.monitor.end_addr + 0x01]
            .chunks(8) {
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.println("");
        }
    }

    fn dump_memory_range(&mut self, start: u16, end: u16) {
        self.console.println("");
        let start = start as usize;
        let end = end as usize;
        for chunk in self.cpu.memory[start..end + 0x01].chunks(8) {
            for b in chunk {
                self.console.print(format!("{:02X} ", *b));
            }
            self.console.println("");
        }
    }

    fn dump_registers(&mut self) {
        self.console.println("");
        self.console.println(format!("A: {} ({:04X})", self.cpu.registers.A, self.cpu.registers.A));
        self.console.println(format!("X: {} ({:04X})", self.cpu.registers.X, self.cpu.registers.X));
        self.console.println(format!("Y: {} ({:04X})", self.cpu.registers.Y, self.cpu.registers.Y));
        self.console.println(format!("PC: {} ({:04X})",
                                     self.cpu.registers.PC,
                                     self.cpu.registers.PC));
        self.console.println(format!("S: {} ({:04X})",
                                     self.cpu.stack.pointer,
                                     self.cpu.stack.pointer));
    }

    fn get_local_segment(&self, pc: usize) -> &CodeSegment {
        for segment in &self.segments {
            let addr = segment.address as usize;
            if pc >= addr && pc <= addr + segment.code.len() {
                return segment;
            }
        }

        &self.segments[0]
    }

    fn highlight_lines(&self,
                       pc: usize,
                       pairs: Vec<(String, u16)>,
                       segment_start: u16,
                       limit_results: bool)
                       -> Vec<String> {
        let mut result = Vec::new();

        let base = pc as isize - segment_start as isize;

        for pair in pairs {
            if limit_results {
                let start = if base > 0x0A { base - 0x0A } else { 0 };
                if (pair.1 as isize) < start || (pair.1 as isize) > base + 0x0A {
                    continue;
                }
            }
            let current_line = pc as u16 == segment_start + pair.1;
            let breakpoint = self.breakpoints[segment_start as usize + pair.1 as usize] > 0x00;

            if breakpoint && current_line {
                result.push(format!("> * {}", pair.0));
            } else if breakpoint && !current_line {
                result.push(format!("  * {}", pair.0));
            } else if !breakpoint && current_line {
                result.push(format!(">   {}", pair.0));
            } else {
                result.push(format!("    {}", pair.0));
            }
        }

        result
    }
}
<|endoftext|>


<commit_before>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
    let path = prepare(filename);
    let mut file = File::create(&path).unwrap();
    file.write(content).unwrap();
    path
}

fn test_file() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_file.txt");

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let bytes = b"Hello, entire World!\n";
    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let bytes = b"Hello, meta-World!\n";
    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
    let symlink_path = prepare("miri_test_fs_symlink.txt");

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_errors.txt");

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);
    rename(&path1, &path2).unwrap();
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    assert!(path2.metadata().unwrap().is_file());
    remove_file(&path2).unwrap();
}
<commit_msg>Test error case of std::fs::rename<commit_after>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
    let path = prepare(filename);
    let mut file = File::create(&path).unwrap();
    file.write(content).unwrap();
    path
}

fn test_file() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_file.txt");

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let bytes = b"Hello, entire World!\n";
    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let bytes = b"Hello, meta-World!\n";
    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
    let symlink_path = prepare("miri_test_fs_symlink.txt");

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_errors.txt");

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);

    // Renaming should succeed
    rename(&path1, &path2).unwrap();
    // Check that the old file path isn't present
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    // Check that the file has moved successfully
    assert!(path2.metadata().unwrap().is_file());

    // Renaming a nonexistent file should fail
    assert_eq!(ErrorKind::NotFound, rename(&path1, &path2).unwrap_err().kind());

    remove_file(&path2).unwrap();
}
<|endoftext|>


<commit_before>//! Asynchronous filesystem manipulation operations (and stdin, stdout, stderr).
//!
//! This module contains basic methods and types for manipulating the contents
//! of the local filesystem from within the context of the Tokio runtime.
//!
//! Tasks running on the Tokio runtime are expected to be asynchronous, i.e.,
//! they will not block the thread of execution. Filesystem operations do not
//! satisfy this requirement. In order to perform filesystem operations
//! asynchronously, this library uses the [`blocking`][blocking] annotation
//! to signal to the runtime that a blocking operation is being performed. This
//! allows the runtime to compensate.
//!
//! [blocking]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/fn.blocking.html

#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.1")]

#[macro_use]
extern crate futures;
extern crate tokio_io;
extern crate tokio_threadpool;

pub mod file;
mod metadata;
mod stdin;
mod stdout;
mod stderr;

pub use file::File;
pub use file::OpenOptions;
pub use metadata::{metadata, MetadataFuture};
pub use stdin::{stdin, Stdin};
pub use stdout::{stdout, Stdout};
pub use stderr::{stderr, Stderr};

use futures::Poll;
use futures::Async::*;

use std::io;
use std::io::ErrorKind::{Other, WouldBlock};

fn blocking_io<F, T>(f: F) -> Poll<T, io::Error>
where F: FnOnce() -> io::Result<T>,
{
    match tokio_threadpool::blocking(f) {
        Ok(Ready(Ok(v))) => Ok(v.into()),
        Ok(Ready(Err(err))) => Err(err),
        Ok(NotReady) => Ok(NotReady),
        Err(_) => Err(blocking_err()),
    }
}

fn would_block<F, T>(f: F) -> io::Result<T>
where F: FnOnce() -> io::Result<T>,
{
    match tokio_threadpool::blocking(f) {
        Ok(Ready(Ok(v))) => Ok(v),
        Ok(Ready(Err(err))) => {
            debug_assert_ne!(err.kind(), WouldBlock);
            Err(err)
        }
        Ok(NotReady) => Err(WouldBlock.into()),
        Err(_) => Err(blocking_err()),
    }
}

fn blocking_err() -> io::Error {
    io::Error::new(Other, "`blocking` annotated I/O must be called \
                   from the context of the Tokio runtime.")
}
<commit_msg>Additional details for tokio-fs rustdoc (#454)<commit_after>//! Asynchronous file and standard stream adaptation.
//!
//! This module contains utility methods and adapter types for input/output to
//! files or standard streams (`Stdin`, `Stdout`, `Stderr`), and
//! filesystem manipulation, for use within (and only within) a Tokio runtime.
//!
//! Tasks run by *worker* threads should not block, as this could delay
//! servicing reactor events. Portable filesystem operations are blocking,
//! however. This module offers adapters which use a [`blocking`] annotation
//! to inform the runtime that a blocking operation is required. When
//! necessary, this allows the runtime to convert the current thread from a
//! *worker* to a *backup* thread, where blocking is acceptable.
//!
//! ## Usage
//!
//! Where possible, users should prefer the provided asynchronous-specific
//! traits such as [`AsyncRead`], or methods returning a `Future` or `Poll`
//! type. Adaptions also extend to traits like `std::io::Read` where methods
//! return `std::io::Result`.  Be warned that these adapted methods may return
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
//! to a *backup* thread immediately. See [tokio-threadpool] for more details
//! of the threading model and [`blocking`].
//!
//! [`blocking`]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool/fn.blocking.html
//! [`AsyncRead`]: https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html
//! [tokio-threadpool]: https://docs.rs/tokio-threadpool/0.1/tokio_threadpool

#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-fs/0.1.1")]

#[macro_use]
extern crate futures;
extern crate tokio_io;
extern crate tokio_threadpool;

pub mod file;
mod metadata;
mod stdin;
mod stdout;
mod stderr;

pub use file::File;
pub use file::OpenOptions;
pub use metadata::{metadata, MetadataFuture};
pub use stdin::{stdin, Stdin};
pub use stdout::{stdout, Stdout};
pub use stderr::{stderr, Stderr};

use futures::Poll;
use futures::Async::*;

use std::io;
use std::io::ErrorKind::{Other, WouldBlock};

fn blocking_io<F, T>(f: F) -> Poll<T, io::Error>
where F: FnOnce() -> io::Result<T>,
{
    match tokio_threadpool::blocking(f) {
        Ok(Ready(Ok(v))) => Ok(v.into()),
        Ok(Ready(Err(err))) => Err(err),
        Ok(NotReady) => Ok(NotReady),
        Err(_) => Err(blocking_err()),
    }
}

fn would_block<F, T>(f: F) -> io::Result<T>
where F: FnOnce() -> io::Result<T>,
{
    match tokio_threadpool::blocking(f) {
        Ok(Ready(Ok(v))) => Ok(v),
        Ok(Ready(Err(err))) => {
            debug_assert_ne!(err.kind(), WouldBlock);
            Err(err)
        }
        Ok(NotReady) => Err(WouldBlock.into()),
        Err(_) => Err(blocking_err()),
    }
}

fn blocking_err() -> io::Error {
    io::Error::new(Other, "`blocking` annotated I/O must be called \
                   from the context of the Tokio runtime.")
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 srither-solver developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

// a & a: same side, a & A: Different side
pub const THEOREM_DEFINE: &'static [&'static str] = &[r"
+ + ! +x+
 0  ! x0x
+ + ! +x+
",
                                                      r"
+ + + + ! + + + +
        !   | x
+ + + + ! +x+-+x+
 0 3    ! x0x3|
+ + + + ! +x+-+x+
        !   | x
+ + + + ! + + + +
",
                                                      r"
+ + + + ! +x+ + +
 0      ! x0x
+ + + + ! +x+-+ +
   3    !   |3 a
+ + + + ! + + + +
        !    A
+ + + + ! + + + +
",
                                                      r"
+ + + + + ! + + + + + ! + + + + +
          !     xa    !    1 1
+ + + + + ! + + + + + ! + +-+-+ +
   3 3    !   |3|3|   !  1|3 3|1
+ + + + + ! + + + + + ! + +-+-+ +
          !     xA    !    1 1
+ + + + + ! + + + + + ! + + + + +
",
                                                      r"
+ + + ! +-+ +
 2    !  2xa
+ + + ! + + +
 3 3  ! |3|3|
+ + + ! + + +
      !   xA
+ + + ! + + +
",
                                                      r"
+ + + + + ! + + + + +
          !   x
+ + + + + ! +x+-+ + +
   3      !   |3 a
+ + + + + ! + + + + +
     3    !    A 3|
+ + + + + ! + + +-+x+
          !       x
+ + + + + ! + + + + +
",
                                                      r"
+ + + + + + ! + + + + + +
            !   x     x
+ + + + + + ! +x+-+x+-+x+
   3   3    !   |3 a 3|
+ + + + + + ! + + + + + +
     3      !    A|3|A
+ + + + + + ! + +x+-+x+ +
            !     x x
+ + + + + + ! + + + + + +
",
                                                      r"
+ + + + ! + + + + ! + + + +
        !         !    1
+ + + + ! + +x+ + ! + +-+ +
  | |   !   | |   !  1|4|1
+ +-+ + ! +x+-+x+ ! + +-+ +
        !   x x   !    1
+ + + + ! + + + + ! + + + +
",
                                                      r"
+-+ ! +-+
 1  ! x1x
+ + ! +x+
",
                                                      r"
+ + ! +-+
x1x ! x1x
+x+ ! +x+
",
                                                      r"
+ + + ! +x+ +
 2|   ! x2|
+-+ + ! +-+x+
      !   x
+ + + ! + + +
",
                                                      r"
+ + ! +x+
|2| ! |2|
+ + ! +x+
",
                                                      r"
+x+ + ! +x+ +
x2    ! x2|
+ + + ! +-+x+
      !   x
+ + + ! + + +
",
                                                      r"
+x+ ! +x+
 2  ! |2|
+x+ ! +x+
",
                                                      r"
+ + + + ! + +x+ +
  |3|   !   |3|
+ +-+ + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ +x+ + ! + +x+ +
   3    !   |3|
+ + + + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
        !   x x
+ + + + ! +x+-+x+
   4    !   |4|
+ + + + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
      !   x
+-+ + ! +-+x+
  |   !   |
+ + + ! + + +
",
                                                      r"
+ + + ! + + +
      !   x
+-+-+ ! +-+-+
      !   x
+ + + ! + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +x+ +
 a 1    !  ax1 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 a 2    !  a 2 A
+ + + + ! + + + +
        !    A
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !   xa
+ + + + ! +x+-+ +
 a 3    !  a|3 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
   a  !    a
+ + + ! + + +
 A 1  !  A 1x
+ + + ! + +x+
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 A 2    !  A 2 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 A 3    !  A 3|
+ + + + ! + +-+x+
        !     x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
  x   !   x
+ + + ! +-+ +
 3 1  !  3 1x
+ + + ! + +x+
",
                                                      r"
+ + + + + ! + + +-+ +
     2x   !    a 2x
+ + + + + ! + + + + +
   3      !   |3 A
+ + + + + ! +x+-+ + +
          !   x
+ + + + + ! + + + + +
",
                                                      r"
+ + + + ! + +-+ +
   2x   !    2x
+ + +-+ ! + + +-+
   3    !   |3
+ + + + ! +x+-+ +
        !   x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
    | !     |
+ + + ! + + +
   3  !   |3
+ + + ! +x+-+
      !   x
+ + + ! + + +
",
                                                      r"
+ +  + + ! + +  + +
      a  !       a
+ +  + + ! + +  + +
   3a    !   |3a
+ +  + + ! +x+--+ +
         !   x
+ +  + + ! + +  + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +x+ +
   1    !  b 1 B
+ + + + ! + +x+ +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
   2    !  A 2 A
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +-+ +
   3    !  b 3 B
+ + + + ! + +-+ +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + ! + +
 a  !  a
+ + ! + +
 1  ! x1x
+ + ! + +
 A  !  A
+ + ! + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
   2    !  b 2 B
+ + + + ! + + + +
   A    !    A
+ + + + ! + + + +
",
                                                      r"
+ + ! + +
 a  !  a
+ + ! + +
 3  ! |3|
+ + ! + +
 A  !  A
+ + ! + +
"];

#[cfg(test)]
mod tests {
    use model::theorem::Theorem;

    #[test]
    fn parse() {
        for s in super::THEOREM_DEFINE {
            if !s.parse::<Theorem>().is_ok() {
                println!("{:?}", s.parse::<Theorem>());
                println!("{}", s);
            }
            assert!(s.parse::<Theorem>().is_ok());
        }
    }
}
<commit_msg>solver: fix test failure<commit_after>// Copyright (c) 2016 srither-solver developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

// a & a: same side, a & A: Different side
pub const THEOREM_DEFINE: &'static [&'static str] = &[r"
+ + ! +x+
 0  ! x0x
+ + ! +x+
",
                                                      r"
+ + + + ! + + + +
        !   | x
+ + + + ! +x+-+x+
 0 3    ! x0x3|
+ + + + ! +x+-+x+
        !   | x
+ + + + ! + + + +
",
                                                      r"
+ + + + ! +x+ + +
 0      ! x0x
+ + + + ! +x+-+ +
   3    !   |3 a
+ + + + ! + + + +
        !    A
+ + + + ! + + + +
",
                                                      r"
+ + + + + ! + + + + + ! + + + + +
          !     xa    !    1 1
+ + + + + ! + + + + + ! + +-+-+ +
   3 3    !   |3|3|   !  1|3 3|1
+ + + + + ! + + + + + ! + +-+-+ +
          !     xA    !    1 1
+ + + + + ! + + + + + ! + + + + +
",
                                                      r"
+ + + ! +-+ +
 2    !  2xa
+ + + ! + + +
 3 3  ! |3|3|
+ + + ! + + +
      !   xA
+ + + ! + + +
",
                                                      r"
+ + + + + ! + + + + +
          !   x
+ + + + + ! +x+-+ + +
   3      !   |3 a
+ + + + + ! + + + + +
     3    !    A 3|
+ + + + + ! + + +-+x+
          !       x
+ + + + + ! + + + + +
",
                                                      r"
+ + + + + + ! + + + + + +
            !   x     x
+ + + + + + ! +x+-+x+-+x+
   3   3    !   |3 a 3|
+ + + + + + ! + + + + + +
     3      !    A|3|A
+ + + + + + ! + +x+-+x+ +
            !     x x
+ + + + + + ! + + + + + +
",
                                                      r"
+ + + + ! + + + + ! + + + +
        !         !    1
+ + + + ! + +x+ + ! + +-+ +
  | |   !   | |   !  1|4|1
+ +-+ + ! +x+-+x+ ! + +-+ +
        !   x x   !    1
+ + + + ! + + + + ! + + + +
",
                                                      r"
+-+ ! +-+
 1  ! x1x
+ + ! +x+
",
                                                      r"
+ + ! +-+
x1x ! x1x
+x+ ! +x+
",
                                                      r"
+ + + ! +x+ +
 2|   ! x2|
+-+ + ! +-+x+
      !   x
+ + + ! + + +
",
                                                      r"
+ + ! +x+
|2| ! |2|
+ + ! +x+
",
                                                      r"
+x+ + ! +x+ +
x2    ! x2|
+ + + ! +-+x+
      !   x
+ + + ! + + +
",
                                                      r"
+x+ ! +x+
 2  ! |2|
+x+ ! +x+
",
                                                      r"
+ + + + ! + +x+ +
  |3|   !   |3|
+ +-+ + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ +x+ + ! + +x+ +
   3    !   |3|
+ + + + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
        !   x x
+ + + + ! +x+-+x+
   4    !   |4|
+ + + + ! +x+-+x+
        !   x x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
      !   x
+-+ + ! +-+x+
  |   !   |
+ + + ! + + +
",
                                                      r"
+ + + ! + + +
      !   x
+-+-+ ! +-+-+
      !   x
+ + + ! + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +x+ +
 a 1    !  ax1 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 a 2    !  a 2 A
+ + + + ! + + + +
        !    A
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !   xa
+ + + + ! +x+-+ +
 a 3    !  a|3 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
   a  !    a
+ + + ! + + +
 A 1  !  A 1x
+ + + ! + +x+
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 A 2    !  A 2 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
 A 3    !  A 3|
+ + + + ! + +-+x+
        !     x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
  x   !   x
+ + + ! +-+ +
 3 1  !  3 1x
+ + + ! + +x+
",
                                                      r"
+ + + + + ! + + +-+ +
     2x   !    a 2x
+ + + + + ! + + + + +
   3      !   |3 A
+ + + + + ! +x+-+ + +
          !   x
+ + + + + ! + + + + +
",
                                                      r"
+ + + + ! + +-+ +
   2x   !    2x
+ + +-+ ! + + +-+
   3    !   |3
+ + + + ! +x+-+ +
        !   x
+ + + + ! + + + +
",
                                                      r"
+ + + ! + + +
    | !     |
+ + + ! + + +
   3  !   |3
+ + + ! +x+-+
      !   x
+ + + ! + + +
",
                                                      r"
+ +  + + ! + +  + +
      a  !       a
+ +  + + ! + +  + +
   3a    !   |3a
+ +  + + ! +x+--+ +
         !   x
+ +  + + ! + +  + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +x+ +
   1    !  b 1 B
+ + + + ! + +x+ +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
   2    !  A 2 A
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + +-+ +
   3    !  b 3 B
+ + + + ! + +-+ +
   a    !    a
+ + + + ! + + + +
",
                                                      r"
+ + ! + +
 a  !  a
+ + ! + +
 1  ! x1x
+ + ! + +
 A  !  A
+ + ! + +
",
                                                      r"
+ + + + ! + + + +
   a    !    a
+ + + + ! + + + +
   2    !  b 2 B
+ + + + ! + + + +
   A    !    A
+ + + + ! + + + +
",
                                                      r"
+ + ! + +
 a  !  a
+ + ! + +
 3  ! |3|
+ + ! + +
 A  !  A
+ + ! + +
"];

#[cfg(test)]
mod tests {
    use model::Theorem;

    #[test]
    fn parse() {
        for s in super::THEOREM_DEFINE {
            if !s.parse::<Theorem>().is_ok() {
                println!("{:?}", s.parse::<Theorem>());
                println!("{}", s);
            }
            assert!(s.parse::<Theorem>().is_ok());
        }
    }
}
<|endoftext|>


<commit_before>///Lazy git tool
use std::env::args as cmd_args;
use std::process::{Command, Stdio};

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    (all_fd=>$fd:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($fd).stderr($fd).status().unwrap(); };
    (stdout=>$stdout:expr, stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).stderr($stderr).status().unwrap(); };
    (stdout=>$stdout:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).status().unwrap(); };
    (stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stderr($stderr).status().unwrap(); };
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); };
}

///Print with prefix >>>
macro_rules! trace {
    ($($arg:expr),*) => { println!(">>>{}", [$(format!("{}", $arg),)*].connect(" ")); }
}

#[inline(always)]
fn usage() {
    println!("lgit [option]\n");
    println!("options:");
    println!("  amend [edit] - amend all changes into last commit");
    println!("  add - add all changes");
    println!("  clean - undo all changes");
    println!("  push [force] - push current branch");
    println!("  commit [message] - commit with message(newline symbols can be used)");
    println!("  fetch - get updates from upstream\n");
}

///Amend changes
fn git_amend() {
    git_add();
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 == "edit" { exec_git_cmd!("commit", "--amend"); }
        else { trace!("Incorrect amend argument:", arg3); }
    }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push() {
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
        else { trace!("Incorrect push argument:", arg3); }
    }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Commit with message
fn git_commit() {
    if cmd_args().skip(2).len() == 0 {
        trace!("Empty commit message");
    }
    else {
        let args: Vec<String> = cmd_args().skip(2).collect();
        exec_git_cmd!("commit", "-m", args.connect(" ").replace("\\n", "\n"));
    }
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    trace!("Unexpected argument:", arg);
    usage();
}

fn main() {
    //To check if git repo present
    if !exec_git_cmd!(stderr=>Stdio::null(), "rev-parse", "-q").success() {
        trace!("Not a git repository");
        return;
    }
    else if cmd_args().len() < 2 {
        usage();
        return;
    }

    match cmd_args().skip(1).next().unwrap().as_ref() {
        "amend"  => git_amend(),
        "fetch"  => git_fetch(),
        "push"   => git_push(),
        "add"    => git_add(),
        "clean"  => git_clean(),
        "commit" => git_commit(),
        arg @ _  => unexpected(&arg),
    }
}
<commit_msg>Rust: lgit add subj option<commit_after>///Lazy git tool
use std::env::args as cmd_args;
use std::process::{Command, Stdio};

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    (all_fd=>$fd:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($fd).stderr($fd).status().unwrap(); };
    (stdout=>$stdout:expr, stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).stderr($stderr).status().unwrap(); };
    (stdout=>$stdout:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).status().unwrap(); };
    (stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stderr($stderr).status().unwrap(); };
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); };
}

///Print with prefix >>>
macro_rules! trace {
    ($($arg:expr),*) => { println!(">>>{}", [$(format!("{}", $arg),)*].connect(" ")); }
}

#[inline(always)]
fn usage() {
    println!("lgit [option]\n");
    println!("options:");
    println!("  amend [edit] - amend all changes into last commit");
    println!("  add - add all changes");
    println!("  clean - undo all changes");
    println!("  push [force] - push current branch");
    println!("  commit [title] [--subj description] - commit with message(option subj place commit description)");
    println!("  fetch - get updates from upstream\n");
}

///Amend changes
fn git_amend() {
    git_add();
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 == "edit" { exec_git_cmd!("commit", "--amend"); }
        else { trace!("Incorrect amend argument:", arg3); }
    }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push() {
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
        else { trace!("Incorrect push argument:", arg3); }
    }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Commit with message
fn git_commit() {
    if cmd_args().skip(2).len() == 0 {
        trace!("Empty commit message");
    }
    else {
        let args: Vec<String> = cmd_args().skip(2).collect();
        let mut args_split = args.split(|elem| elem.to_lowercase() == "--subj");

        //If no --subj split will have only one element.
        //Consider this case as the whole message.
        let title: String = args_split.next().unwrap().connect(" ");
        if let Some(subj_array) = args_split.next() {
            let mut subj: String = subj_array.connect(" ").replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            subj.pop();
            exec_git_cmd!("commit", "-m", format!("{}\n\n{}", title, subj));
        }
        else {
            let mut title = title.replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            title.pop();
            exec_git_cmd!("commit", "-m", title);
        }

    }
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    trace!("Unexpected argument:", arg);
    usage();
}

fn main() {
    //To check if git repo present
    if !exec_git_cmd!(stderr=>Stdio::null(), "rev-parse", "-q").success() {
        trace!("Not a git repository");
        return;
    }
    else if cmd_args().len() < 2 {
        usage();
        return;
    }

    match cmd_args().skip(1).next().unwrap().as_ref() {
        "amend"  => git_amend(),
        "fetch"  => git_fetch(),
        "push"   => git_push(),
        "add"    => git_add(),
        "clean"  => git_clean(),
        "commit" => git_commit(),
        arg @ _  => unexpected(&arg),
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write, BufWriter};
use std::borrow::Cow;
use std::sync::Mutex;
use std::{fs, fmt};

use std::collections::HashMap;

use log::{self, Log};

use {FernLog, Formatter, Filter};

pub enum LevelConfiguration {
    JustDefault,
    Minimal(Vec<(Cow<'static, str>, log::LogLevelFilter)>),
    Many(HashMap<Cow<'static, str>, log::LogLevelFilter>),
}

pub struct Dispatch {
    pub output: Vec<Output>,
    pub default_level: log::LogLevelFilter,
    pub levels: LevelConfiguration,
    pub format: Option<Box<Formatter>>,
    pub filters: Vec<Box<Filter>>,
}

/// Callback struct for use within a formatter closure
///
/// Callbacks are used for formatting in order to allow usage of [`std::fmt`]-based formatting without
/// the allocation of the formatted result which would be required to return it.
///
/// Example usage:
///
/// ```
/// fern::Dispatch::new()
///     .format(|callback: fern::FormatCallback, message, record| {
///         callback.finish(format_args!("[{}] {}", record.level(), message))
///     })
///     # ;
/// ```
#[must_use = "format callback must be used for log to process correctly"]
pub struct FormatCallback<'a>(InnerFormatCallback<'a>);

struct InnerFormatCallback<'a>(&'a mut bool, &'a Dispatch, &'a log::LogRecord<'a>);

pub enum Output {
    Stdout(Stdout),
    Stderr(Stderr),
    File(File),
    Dispatch(Dispatch),
    Other(Box<FernLog>),
}

pub struct Stdout {
    pub stream: io::Stdout,
    pub line_sep: Cow<'static, str>,
}

pub struct Stderr {
    pub stream: io::Stderr,
    pub line_sep: Cow<'static, str>,
}

pub struct File {
    pub stream: Mutex<BufWriter<fs::File>>,
    pub line_sep: Cow<'static, str>,
}

pub struct Null;

impl From<Vec<(Cow<'static, str>, log::LogLevelFilter)>> for LevelConfiguration {
    fn from(mut levels: Vec<(Cow<'static, str>, log::LogLevelFilter)>) -> Self {
        // Benchmarked separately: https://gist.github.com/daboross/976978d8200caf86e02acb6805961195
        // Use Vec if there are fewer than 15 items, HashMap if there are more than 15.
        match levels.len() {
            0 => LevelConfiguration::JustDefault,
            x if x > 15 => LevelConfiguration::Many(levels.into_iter().collect()),
            _ => {
                levels.shrink_to_fit();
                LevelConfiguration::Minimal(levels)
            }
        }
    }
}

impl LevelConfiguration {
    #[inline]
    fn find(&self, module: &str) -> Option<log::LogLevelFilter> {
        match *self {
            LevelConfiguration::JustDefault => None,
            LevelConfiguration::Minimal(ref levels) => {
                levels.iter().find(|&&(ref test_module, _)| test_module == module).map(|&(_, level)| level)
            }
            LevelConfiguration::Many(ref levels) => levels.get(module).cloned(),
        }
    }
}

impl FernLog for Output {
    fn log_args(&self, input: &fmt::Arguments, record: &log::LogRecord) {
        match *self {
            Output::Stdout(ref s) => s.log_args(input, record),
            Output::Stderr(ref s) => s.log_args(input, record),
            Output::File(ref s) => s.log_args(input, record),
            Output::Dispatch(ref s) => s.log_args(input, record),
            Output::Other(ref s) => s.log_args(input, record),
        }
    }
}

impl log::Log for Dispatch {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= self.levels.find(metadata.target()).unwrap_or(self.default_level) &&
        self.filters.iter().all(|f| f(metadata))
    }

    fn log(&self, record: &log::LogRecord) {
        self.log_args(record.args(), record)
    }
}

impl log::Log for Null {
    fn enabled(&self, _: &log::LogMetadata) -> bool {
        false
    }

    fn log(&self, _: &log::LogRecord) {}
}

impl FernLog for Dispatch {
    fn log_args(&self, message: &fmt::Arguments, record: &log::LogRecord) {
        if self.enabled(record.metadata()) {
            match self.format {
                Some(ref format) => {
                    // flag to ensure the log message is completed even if the formatter doesn't complete the callback.
                    let mut callback_called_flag = false;

                    (format)(FormatCallback(InnerFormatCallback(&mut callback_called_flag, self, record)),
                             message,
                             record);

                    if !callback_called_flag {
                        self.finish_logging(message, record);
                    }
                }
                None => {
                    self.finish_logging(message, record);
                }
            }
        }
    }
}

impl Dispatch {
    fn finish_logging(&self, formatted_message: &fmt::Arguments, record: &log::LogRecord) {
        for log in &self.output {
            log.log_args(formatted_message, record);
        }
    }
}

impl<'a> FormatCallback<'a> {
    /// Complete the formatting call that this FormatCallback was created for.
    ///
    /// This will call the rest of the logging chain using the given formatted message
    /// as the new payload message.
    ///
    /// Example usage:
    ///
    /// ```
    /// # fern::Dispatch::new()
    /// # .format(|callback: fern::FormatCallback, message, record| {
    /// callback.finish(format_args!("[{}] {}", record.level(), message))
    /// # })
    /// # .into_log();
    /// ```
    ///
    /// See [`format_args!`]
    ///
    /// [`format_args!`]: https://doc.rust-lang.org/std/macro.format_args.html
    pub fn finish(self, formatted_message: fmt::Arguments) {
        let FormatCallback(InnerFormatCallback(callback_called_flag, dispatch, record)) = self;

        // let the dispatch know that we did in fact get called.
        *callback_called_flag = true;

        dispatch.finish_logging(&formatted_message, record);
    }
}

// No need to write this twice (used for Stdout and Stderr structs)
macro_rules! std_log_impl {
    ($ident:ident) => {
        impl FernLog for $ident {
            fn log_args(&self, payload: &fmt::Arguments, record: &log::LogRecord) {
                fallback_on_error(payload, record, |payload, _| {
                    write!(self.stream.lock(), "{}{}", payload, self.line_sep)
                });
            }
        }
    };
}

std_log_impl!(Stdout);
std_log_impl!(Stderr);

impl FernLog for File {
    fn log_args(&self, payload: &fmt::Arguments, record: &log::LogRecord) {
        fallback_on_error(payload, record, |payload, _| {
            let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());

            write!(writer, "{}{}", payload, self.line_sep)?;

            writer.flush()?;

            Ok(())
        });
    }
}

#[inline(always)]
fn fallback_on_error<F>(payload: &fmt::Arguments, record: &log::LogRecord, log_func: F)
    where F: FnOnce(&fmt::Arguments, &log::LogRecord) -> io::Result<()>
{
    if let Err(error) = log_func(payload, record) {
        backup_logging(payload, record, error)
    }
}

fn backup_logging(payload: &fmt::Arguments, record: &log::LogRecord, error: io::Error) {
    let second = write!(io::stderr(),
                        "Error performing logging.\
                            \n\tattempted to log: {}\
                            \n\torigin location: {:#?}\
                            \n\tlogging error: {}",
                        payload,
                        record.location(),
                        error);

    if let Err(second_error) = second {
        panic!("Error performing stderr logging after error occurred during regular logging.\
                \n\tattempted to log: {}\
                \n\torigin location: {:#?}\
                \n\tfirst logging Error: {}\
                \n\tstderr error: {}",
               payload,
               record.location(),
               error,
               second_error);
    }
}
<commit_msg>Fix up FormatCallback docs more<commit_after>use std::io::{self, Write, BufWriter};
use std::borrow::Cow;
use std::sync::Mutex;
use std::{fs, fmt};

use std::collections::HashMap;

use log::{self, Log};

use {FernLog, Formatter, Filter};

pub enum LevelConfiguration {
    JustDefault,
    Minimal(Vec<(Cow<'static, str>, log::LogLevelFilter)>),
    Many(HashMap<Cow<'static, str>, log::LogLevelFilter>),
}

pub struct Dispatch {
    pub output: Vec<Output>,
    pub default_level: log::LogLevelFilter,
    pub levels: LevelConfiguration,
    pub format: Option<Box<Formatter>>,
    pub filters: Vec<Box<Filter>>,
}

/// Callback struct for use within a formatter closure
///
/// Callbacks are used for formatting in order to allow usage of [`std::fmt`]-based formatting without
/// the allocation of the formatted result which would be required to return it.
///
/// Example usage:
///
/// ```
/// fern::Dispatch::new()
///     .format(|callback: fern::FormatCallback, message, record| {
///         callback.finish(format_args!("[{}] {}", record.level(), message))
///     })
///     # ;
/// ```
///
/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
#[must_use = "format callback must be used for log to process correctly"]
pub struct FormatCallback<'a>(InnerFormatCallback<'a>);

struct InnerFormatCallback<'a>(&'a mut bool, &'a Dispatch, &'a log::LogRecord<'a>);

pub enum Output {
    Stdout(Stdout),
    Stderr(Stderr),
    File(File),
    Dispatch(Dispatch),
    Other(Box<FernLog>),
}

pub struct Stdout {
    pub stream: io::Stdout,
    pub line_sep: Cow<'static, str>,
}

pub struct Stderr {
    pub stream: io::Stderr,
    pub line_sep: Cow<'static, str>,
}

pub struct File {
    pub stream: Mutex<BufWriter<fs::File>>,
    pub line_sep: Cow<'static, str>,
}

pub struct Null;

impl From<Vec<(Cow<'static, str>, log::LogLevelFilter)>> for LevelConfiguration {
    fn from(mut levels: Vec<(Cow<'static, str>, log::LogLevelFilter)>) -> Self {
        // Benchmarked separately: https://gist.github.com/daboross/976978d8200caf86e02acb6805961195
        // Use Vec if there are fewer than 15 items, HashMap if there are more than 15.
        match levels.len() {
            0 => LevelConfiguration::JustDefault,
            x if x > 15 => LevelConfiguration::Many(levels.into_iter().collect()),
            _ => {
                levels.shrink_to_fit();
                LevelConfiguration::Minimal(levels)
            }
        }
    }
}

impl LevelConfiguration {
    #[inline]
    fn find(&self, module: &str) -> Option<log::LogLevelFilter> {
        match *self {
            LevelConfiguration::JustDefault => None,
            LevelConfiguration::Minimal(ref levels) => {
                levels.iter().find(|&&(ref test_module, _)| test_module == module).map(|&(_, level)| level)
            }
            LevelConfiguration::Many(ref levels) => levels.get(module).cloned(),
        }
    }
}

impl FernLog for Output {
    fn log_args(&self, input: &fmt::Arguments, record: &log::LogRecord) {
        match *self {
            Output::Stdout(ref s) => s.log_args(input, record),
            Output::Stderr(ref s) => s.log_args(input, record),
            Output::File(ref s) => s.log_args(input, record),
            Output::Dispatch(ref s) => s.log_args(input, record),
            Output::Other(ref s) => s.log_args(input, record),
        }
    }
}

impl log::Log for Dispatch {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= self.levels.find(metadata.target()).unwrap_or(self.default_level) &&
        self.filters.iter().all(|f| f(metadata))
    }

    fn log(&self, record: &log::LogRecord) {
        self.log_args(record.args(), record)
    }
}

impl log::Log for Null {
    fn enabled(&self, _: &log::LogMetadata) -> bool {
        false
    }

    fn log(&self, _: &log::LogRecord) {}
}

impl FernLog for Dispatch {
    fn log_args(&self, message: &fmt::Arguments, record: &log::LogRecord) {
        if self.enabled(record.metadata()) {
            match self.format {
                Some(ref format) => {
                    // flag to ensure the log message is completed even if the formatter doesn't complete the callback.
                    let mut callback_called_flag = false;

                    (format)(FormatCallback(InnerFormatCallback(&mut callback_called_flag, self, record)),
                             message,
                             record);

                    if !callback_called_flag {
                        self.finish_logging(message, record);
                    }
                }
                None => {
                    self.finish_logging(message, record);
                }
            }
        }
    }
}

impl Dispatch {
    fn finish_logging(&self, formatted_message: &fmt::Arguments, record: &log::LogRecord) {
        for log in &self.output {
            log.log_args(formatted_message, record);
        }
    }
}

impl<'a> FormatCallback<'a> {
    /// Complete the formatting call that this FormatCallback was created for.
    ///
    /// This will call the rest of the logging chain using the given formatted message
    /// as the new payload message.
    ///
    /// Example usage:
    ///
    /// ```
    /// # fern::Dispatch::new()
    /// # .format(|callback: fern::FormatCallback, message, record| {
    /// callback.finish(format_args!("[{}] {}", record.level(), message))
    /// # })
    /// # .into_log();
    /// ```
    ///
    /// See [`format_args!`].
    ///
    /// [`format_args!`]: https://doc.rust-lang.org/std/macro.format_args.html
    pub fn finish(self, formatted_message: fmt::Arguments) {
        let FormatCallback(InnerFormatCallback(callback_called_flag, dispatch, record)) = self;

        // let the dispatch know that we did in fact get called.
        *callback_called_flag = true;

        dispatch.finish_logging(&formatted_message, record);
    }
}

// No need to write this twice (used for Stdout and Stderr structs)
macro_rules! std_log_impl {
    ($ident:ident) => {
        impl FernLog for $ident {
            fn log_args(&self, payload: &fmt::Arguments, record: &log::LogRecord) {
                fallback_on_error(payload, record, |payload, _| {
                    write!(self.stream.lock(), "{}{}", payload, self.line_sep)
                });
            }
        }
    };
}

std_log_impl!(Stdout);
std_log_impl!(Stderr);

impl FernLog for File {
    fn log_args(&self, payload: &fmt::Arguments, record: &log::LogRecord) {
        fallback_on_error(payload, record, |payload, _| {
            let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());

            write!(writer, "{}{}", payload, self.line_sep)?;

            writer.flush()?;

            Ok(())
        });
    }
}

#[inline(always)]
fn fallback_on_error<F>(payload: &fmt::Arguments, record: &log::LogRecord, log_func: F)
    where F: FnOnce(&fmt::Arguments, &log::LogRecord) -> io::Result<()>
{
    if let Err(error) = log_func(payload, record) {
        backup_logging(payload, record, error)
    }
}

fn backup_logging(payload: &fmt::Arguments, record: &log::LogRecord, error: io::Error) {
    let second = write!(io::stderr(),
                        "Error performing logging.\
                            \n\tattempted to log: {}\
                            \n\torigin location: {:#?}\
                            \n\tlogging error: {}",
                        payload,
                        record.location(),
                        error);

    if let Err(second_error) = second {
        panic!("Error performing stderr logging after error occurred during regular logging.\
                \n\tattempted to log: {}\
                \n\torigin location: {:#?}\
                \n\tfirst logging Error: {}\
                \n\tstderr error: {}",
               payload,
               record.location(),
               error,
               second_error);
    }
}
<|endoftext|>