<commit_before>extern crate "dbus-rs" as dbus;

use std::os::{args, set_exit_status};
use dbus::{Connection, BusType, Message, MessageItem};

static DEST: &'static str = "org.kde.kdeconnect";
static PATH: &'static str = "/modules/kdeconnect";

fn get_ids(c: &Connection) -> Result<Vec<String>, &'static str> {
    let mut m = Message::new_method_call(DEST
                                         , PATH
                                         , &*[DEST, "daemon"].connect(".")
                                         , "devices").unwrap();
    m.append_items(&[MessageItem::Bool(true)]); // onlyReachable
    m = c.send_with_reply_and_block(m, 1000).unwrap();
    match m.get_items()[0] {
        MessageItem::Array(_, 0) => Err("No reachable devices."),
        MessageItem::Array(ref a, _) => Ok(a.iter().map(|e| match e {
            &MessageItem::Str(ref s) => s.clone(),
            _ => unreachable!(),
        }).collect()),
        _ => unreachable!(),
    }
}

fn share_url(c: &Connection, id: &String, url: &String) {
    let mut m = Message::new_method_call(DEST
                                         , &*[PATH, "devices", &id[], "share"].connect("/")
                                         , &*[DEST, "device.share"].connect(".")
                                         , "shareUrl").unwrap();
    m.append_items(&[MessageItem::Str(url.clone())]);
    c.send_with_reply_and_block(m, 1000).unwrap();
}

fn main() {
    let urls = &args()[1..];
    if urls.is_empty() {
        set_exit_status(1);
        return println!("Usage: {} url [url ...]", &args()[0])
    }

    let ref c = Connection::get_private(BusType::Session).unwrap();
    match get_ids(c) {
        Err(e) => return println!("{}", e),
        Ok(ids) =>
            for url in urls.iter() {
                for id in (*ids).iter() {
                    share_url(c, id, url);
                }
            }
    }
}
<commit_msg>define const instead of static<commit_after>extern crate "dbus-rs" as dbus;

use std::os::{args, set_exit_status};
use dbus::{Connection, BusType, Message, MessageItem};

const DEST: &'static str = "org.kde.kdeconnect";
const PATH: &'static str = "/modules/kdeconnect";

fn get_ids(c: &Connection) -> Result<Vec<String>, &'static str> {
    let mut m = Message::new_method_call(DEST
                                         , PATH
                                         , &*[DEST, "daemon"].connect(".")
                                         , "devices").unwrap();
    m.append_items(&[MessageItem::Bool(true)]); // onlyReachable
    m = c.send_with_reply_and_block(m, 1000).unwrap();
    match m.get_items()[0] {
        MessageItem::Array(_, 0) => Err("No reachable devices."),
        MessageItem::Array(ref a, _) => Ok(a.iter().map(|e| match e {
            &MessageItem::Str(ref s) => s.clone(),
            _ => unreachable!(),
        }).collect()),
        _ => unreachable!(),
    }
}

fn share_url(c: &Connection, id: &String, url: &String) {
    let mut m = Message::new_method_call(DEST
                                         , &*[PATH, "devices", &id[], "share"].connect("/")
                                         , &*[DEST, "device.share"].connect(".")
                                         , "shareUrl").unwrap();
    m.append_items(&[MessageItem::Str(url.clone())]);
    c.send_with_reply_and_block(m, 1000).unwrap();
}

fn main() {
    let urls = &args()[1..];
    if urls.is_empty() {
        set_exit_status(1);
        return println!("Usage: {} url [url ...]", &args()[0])
    }

    let ref c = Connection::get_private(BusType::Session).unwrap();
    match get_ids(c) {
        Err(e) => return println!("{}", e),
        Ok(ids) =>
            for url in urls.iter() {
                for id in (*ids).iter() {
                    share_url(c, id, url);
                }
            }
    }
}
<|endoftext|>


<commit_before>#![feature(lang_items, unboxed_closures)]

use std::io::{File, IoResult, Truncate, Write};
use std::num::Float;
use std::num::FloatMath;
use std::rand;
use std::rand::Rng;

struct SinWave(pub f64);

impl Fn<(f64, ), f64> for SinWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SinWave(frequency) = *self;
        FloatMath::sin(t * frequency)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SinWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_16(y: f64) -> i16 {
    // Quantization levels for 16 bits
    let levels = 2.0.powf(16.0) - 1.0;

    // Convert from [-1, 1] to [-2**16 / 2, 2**16 / 2]
    (y * (levels / 2.0)) as i16
}

fn make_sample_16<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<i16> where F: Fn<(f64, ), f64>+Copy {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<i16> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(quantize_16(generate(t, waveform)));
    }

    samples
}

fn write_pcm(filename: &str, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
fn write_wav(filename: &str, sample_rate: uint, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_be_i32(0x52494646))              // ChunkID, RIFF
    try!(f.write_le_i32(chunk_size));             // ChunkSize
    try!(f.write_be_i32(0x57415645));             // Format, WAVE

    try!(f.write_be_i32(0x666d7420));             // Subchunk1ID, fmt
    try!(f.write_le_i32(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_le_i16(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_le_i16(channels as i16));        // NumChannels
    try!(f.write_le_i32(sample_rate as i32));     // SampleRate
    try!(f.write_le_i32(byte_rate));              // ByteRate
    try!(f.write_le_i16(block_align));            // BlockAlign
    try!(f.write_le_i16(bit_depth as i16));       // BitsPerSample

    try!(f.write_be_i32(0x64617461));             // Subchunk2ID, data
    try!(f.write_le_i32(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample))
    }

    Ok(())
}

fn main() {
    println!("Hello, synthrs!");

    write_pcm("out/sin.pcm", make_sample_16(1.0, 44100, SinWave(1000.0))).ok();
    write_wav("out/sin.wav", 44100, make_sample_16(1.0, 44100, SinWave(1000.0))).ok();
    write_wav("out/square.wav", 44100, make_sample_16(1.0, 44100, SquareWave(1000.0))).ok();
    write_wav("out/sawtooth.wav", 44100, make_sample_16(1.0, 44100, SawtoothWave(1000.0))).ok();

    write_wav("out/wolftone.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        (SquareWave(1000.0)(t) + SquareWave(1020.0)(t)) / 2.0
    })).ok();

    write_wav("out/whitenoise.wav", 44100, make_sample_16(1.0, 44100, |_t: f64| -> f64 {
        let mut rng = rand::task_rng();
        (rng.gen::<f64>() - 0.5) * 2.0
    })).ok();

    write_wav("out/rising.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        let (min_f, max_f) = (1000.0, 8000.0);
        let max_t = 1.0; // Duration of clip in seconds
        let range = max_f - min_f;
        let f = max_f - (max_t - t) * range;
        SinWave(f)(t)
    })).ok();

    write_wav("out/racecar.wav", 44100, make_sample_16(15.0, 44100, |t: f64| -> f64 {
        let mut rng = rand::task_rng();
        let mut out = 0.0;
        if t < 14.0 { out += SawtoothWave(40.63 * (t / 2.0))(t); } // Engine
        if t < 1.0 { out += SawtoothWave(30.0)(t) * 10.0; } // Engine start
        if t < 14.0 && t.tan() > 0.0 { out += SquareWave(t.fract() * 523.25)(t); } // Gear
        if t < 14.0 && t.tan() < 0.0 { out += SquareWave(t.sin() * 1046.5)(t); } // Gear
        if t < 14.0 && t.tan() > 0.866 { out += SquareWave(t.fract() * 2093.0)(t); } // Gear
        if t > 14.0 { out += (rng.gen::<f64>() - 0.5) * 8.0 } // Tree
        (out / 4.0).min(1.0)
    })).ok();
}
<commit_msg>Fix wrong frequency for waves<commit_after>#![feature(lang_items, unboxed_closures)]

use std::io::{File, IoResult, Truncate, Write};
use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;
use std::rand;
use std::rand::Rng;

struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}

fn generate<F>(x: f64, f: F) -> f64 where F: Fn<(f64, ), f64> {
    f(x)
}

fn quantize_16(y: f64) -> i16 {
    // Quantization levels for 16 bits
    let levels = 2.0.powf(16.0) - 1.0;

    // Convert from [-1, 1] to [-2**16 / 2, 2**16 / 2]
    (y * (levels / 2.0)) as i16
}

fn make_sample_16<F>(length: f64, sample_rate: uint, waveform: F) -> Vec<i16> where F: Fn<(f64, ), f64>+Copy {
    let num_samples = (sample_rate as f64 * length).floor() as uint;
    let mut samples: Vec<i16> = Vec::with_capacity(num_samples);

    for i in range(0u, num_samples) {
        let t = i as f64 / sample_rate as f64;
        samples.push(quantize_16(generate(t, waveform)));
    }

    samples
}

fn write_pcm(filename: &str, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
fn write_wav(filename: &str, sample_rate: uint, samples: Vec<i16>) -> IoResult<()> {
    let path = Path::new(filename);
    let mut f = match File::open_mode(&path, Truncate, Write) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_be_i32(0x52494646))              // ChunkID, RIFF
    try!(f.write_le_i32(chunk_size));             // ChunkSize
    try!(f.write_be_i32(0x57415645));             // Format, WAVE

    try!(f.write_be_i32(0x666d7420));             // Subchunk1ID, fmt
    try!(f.write_le_i32(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_le_i16(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_le_i16(channels as i16));        // NumChannels
    try!(f.write_le_i32(sample_rate as i32));     // SampleRate
    try!(f.write_le_i32(byte_rate));              // ByteRate
    try!(f.write_le_i16(block_align));            // BlockAlign
    try!(f.write_le_i16(bit_depth as i16));       // BitsPerSample

    try!(f.write_be_i32(0x64617461));             // Subchunk2ID, data
    try!(f.write_le_i32(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_le_i16(*sample))
    }

    Ok(())
}

fn main() {
    println!("Hello, synthrs!");

    write_pcm("out/sin.pcm", make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/sin.wav", 44100, make_sample_16(1.0, 44100, SineWave(440.0))).ok();
    write_wav("out/square.wav", 44100, make_sample_16(1.0, 44100, SquareWave(440.0))).ok();
    write_wav("out/sawtooth.wav", 44100, make_sample_16(1.0, 44100, SawtoothWave(440.0))).ok();

    write_wav("out/wolftone.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        (SquareWave(1000.0)(t) + SquareWave(1020.0)(t)) / 2.0
    })).ok();

    write_wav("out/whitenoise.wav", 44100, make_sample_16(1.0, 44100, |_t: f64| -> f64 {
        let mut rng = rand::task_rng();
        (rng.gen::<f64>() - 0.5) * 2.0
    })).ok();

    write_wav("out/rising.wav", 44100, make_sample_16(1.0, 44100, |t: f64| -> f64 {
        let (min_f, max_f) = (1000.0, 8000.0);
        let max_t = 1.0; // Duration of clip in seconds
        let range = max_f - min_f;
        let f = max_f - (max_t - t) * range;
        SineWave(f)(t)
    })).ok();

    write_wav("out/racecar.wav", 44100, make_sample_16(15.0, 44100, |t: f64| -> f64 {
        let mut rng = rand::task_rng();
        let mut out = 0.0;
        if t < 14.0 { out += SawtoothWave(20.315 * (t / 2.0))(t); } // Engine
        if t < 1.0 { out += SawtoothWave(15.0)(t) * 10.0; } // Engine start
        if t < 14.0 && t.tan() > 0.0 { out += SquareWave(t.fract() * 261.625)(t); } // Gear
        if t < 14.0 && t.tan() < 0.0 { out += SquareWave(t.sin() * 523.25)(t); } // Gear
        if t < 14.0 && t.tan() > 0.866 { out += SquareWave(t.fract() * 1046.5)(t); } // Gear
        if t > 14.0 { out += (rng.gen::<f64>() - 0.5) * 8.0 } // Tree
        (out / 4.0).min(1.0)
    })).ok();

    write_wav("out/shepard.wav", 44100, make_sample_16(30.0, 44100, |t: f64| -> f64 {
        let length = 10.0;
        let t_mod = t % length;
        let progress = t_mod / length;
        let tone_a2 = SineWave(110.0 / (1.0 + progress));
        let tone_a3 = SineWave(220.0 / (1.0 + progress));
        let tone_a4 = SineWave(440.0 / (1.0 + progress));
        let tone_a5 = SineWave(880.0 / (1.0 + progress));
        (tone_a2(t_mod) * (1.0 - progress)
        + tone_a5(t_mod) * progress
        + tone_a3(t_mod) + tone_a4(t_mod)) / 4.0
    })).ok();
}
<|endoftext|>


<commit_before>mod uci;
pub mod board;
mod tests;
pub mod search_algorithms;
pub mod pgn;
pub mod uci_engine;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate itertools;
extern crate rand;
extern crate ordered_float;
extern crate rayon;
#[cfg(feature = "logging")]
extern crate log4rs;
#[cfg(feature = "profile")]
extern crate cpuprofiler;

use std::io;
use std::fmt;
use std::hash::Hash;
use std::time;

use search_algorithms::alpha_beta::Score;
use search_algorithms::mcts;
use search_algorithms::board::GameResult;
use pgn::PgnBoard;

use board::std_board::ChessBoard;
use board::crazyhouse_board::CrazyhouseBoard;
use board::sjadam_board::SjadamBoard;

#[cfg(feature = "logging")]
use log4rs::config::{Appender, Config, Root};
#[cfg(feature = "logging")]
use std::io::Write;

#[cfg(feature = "profile")]
use cpuprofiler::PROFILER;
use search_algorithms::board::Board;
use search_algorithms::board::ExtendedBoard;
use search_algorithms::alpha_beta::AlphaBeta;
use uci_engine::UciEngine;
use search_algorithms::monte_carlo::MonteCarlo;
use std::sync::Arc;
use std::sync::Mutex;
use search_algorithms::monte_carlo;

#[cfg(feature = "logging")]
fn init_log() -> Result<(), Box<std::error::Error>> {
    let appender = log4rs::append::file::FileAppender::builder().append(true).build("rasher.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("uci", Box::new(appender)))
        .build(Root::builder().appender("uci").build(log::LogLevelFilter::Debug))?;
    log4rs::init_config(config)?;
    Ok(())
}

fn main() {
    #[cfg(feature = "logging")]
    init_log().unwrap_or_else(|err| {
        let _ = writeln!(&mut io::stderr(), "Failed to open log file: {}", err);
    });

    #[cfg(feature = "profile")]
    PROFILER.lock().unwrap().start("./my-prof.profile").unwrap();

    let mut stdin = io::BufReader::new(io::stdin());
    info!("Opened log");

    for &cp in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0].iter() {
        let win_pct = monte_carlo::eval_to_win_pct(cp);
        println!("{} eval: {}", cp, win_pct);
        println!("{} win percentage: {} eval", win_pct, monte_carlo::win_pct_to_eval(win_pct))
    }

    let monte_carlo: MonteCarlo<ChessBoard> = MonteCarlo::init();
    let mut board = ChessBoard::start_board();
    for uci_info in monte_carlo.search(board.clone(),
                                       uci::TimeRestriction::Infinite,
                                       Arc::new(Mutex::new(uci::EngineComm::new())),
                                       None) {
        println!("{}", uci_info.to_info_string(&mut board));
    }

    loop {
        if let Ok(input) = uci::get_engine_input(&mut stdin) {

	    let tokens : Vec<&str> = input.split_whitespace().collect();

            match tokens[0] {
                "uci" => {
                    uci::connect_engine(&mut stdin).unwrap();
                    return;
                },
                "isready" => {
                    info!("received isready from GUI");
                    uci::uci_send("readyok");
                },

                "mem_usage" => {
                    use std::mem;
                    println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                    println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                    println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                    println!("Sjadam board: {}", mem::size_of::<SjadamBoard>());
                    println!("Sjadam move: {}", mem::size_of::<board::sjadam_move::SjadamMove>());
                    println!("Board score: {}", mem::size_of::<Score>());
                    println!("Size of chess mcts node: {}",
                             mem::size_of::<mcts::MonteCarloTree<ChessBoard>>());

                },
                //"play_self" => play_game(&board::START_BOARD.clone()),
                "play_self" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_game(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_game(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_game(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "play" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_human(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_human(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_human(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "mcts" => {

                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let mut board = ChessBoard::start_board().clone();
                        search_algorithms::mcts::search_position(&mut board);
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let mut board = CrazyhouseBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            },
                            "sjadam" => {
                                let mut board = SjadamBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                }
                "mcts_debug" => mcts::play_human(ChessBoard::start_board()),
                s => warn!("Unrecognized command \"{}\".", s),
            }
        }
        else {
            return;
        }
    }
}

/// Makes the engine play a game against itself
fn play_game<B> (mut board : B)
    where B: PgnBoard + ExtendedBoard + fmt::Debug + Send + 'static + Hash + Eq + Clone,
<B as Board>::Move: Sync + Send {
    println!("Board:\n{:?}", board);
    println!("\n");
    match board.game_result() {
        None => {
            let mut engine = AlphaBeta::init();
            let (score, mv) =
                engine.best_move(board.clone(),
                                 uci::TimeRestriction::MoveTime(time::Duration::from_secs(5)),
                                 None).unwrap();

            println!("Found move {:?} with score {}.", mv, score.uci_string(board.side_to_move()));
            board.do_move(mv);
            play_game(board);
        }
        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}
/// Play a game against the engine through stdin
fn play_human<B>(mut board : B)
    where B: 'static + PgnBoard + ExtendedBoard + fmt::Debug + Send + Hash + Eq,
          <B as Board>::Move: Sync + Send
{
    match board.game_result() {
        None => {
            use search_algorithms::board::Color::*;
            println!("Board:\n{:?}", board);
            // If black, play as human
            if board.side_to_move() == White {
                println!("Type your move as long algebraic notation (e2e4):");

                let reader = io::stdin();
                let mut input_str = "".to_string();
                let mut legal_moves = vec![];
                board.generate_moves(&mut legal_moves);
                // Loop until user enters a valid move
                loop {
                    input_str.clear();
                    reader.read_line(&mut input_str).expect("Failed to read line");

                    match board.move_from_lan(input_str.trim()) {
                        Ok(val) => {
                            if legal_moves.contains(&val) { break; }
                            println!("Move {:?} is illegal! Legal moves: {:?}", val, legal_moves);
                            println!("Try again: ");
                        }

                        Err(error) => {
                            println!("{}, try again.", error);
                        },
                    }
                }
                let c_move = board.move_from_lan(input_str.trim()).unwrap();
                board.do_move(c_move);
                play_human(board);
            }
            else {

                let mut engine = AlphaBeta::init();
                let (score, best_move) =
                    engine.best_move(board.clone(),
                                     uci::TimeRestriction::MoveTime(time::Duration::from_secs(5)),
                                     None).unwrap();

                println!("Computer played {:?} with score {}",
                         best_move, score.uci_string(board.side_to_move()));
                board.do_move(best_move);
                play_human(board);
            }
        }

        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern: u64,
    leaf: u64,
    qsearch: u64,
    hash_full_hits: u64, // TT hit causes a cutoff
    hash_partial_hits: u64, // TT hit, hash move is used, but no cutoff
    hash_misses: u64, // No TT hit
    hash_move_cutoffs: u64, // Hash move causes a cutoff before other moves are tried
    null_move_cutoffs: u64,
    null_move_skips: u64,
}

impl NodeCount {
    fn new() -> Self {
        NodeCount { intern: 0, leaf: 0, qsearch: 0, null_move_cutoffs: 0,
            null_move_skips: 0, hash_full_hits: 0,
            hash_partial_hits: 0, hash_misses: 0, hash_move_cutoffs: 0 }
    }
    fn total(&self) -> u64 {
        self.intern + self.leaf + self.qsearch
    }
}

impl std::ops::Add for NodeCount {
    type Output = NodeCount;
    fn add(self, other: NodeCount) -> Self {
        NodeCount {
            intern: self.intern + other.intern, leaf: self.leaf + other.leaf,
            qsearch: self.qsearch + other.qsearch,
            null_move_cutoffs: self.null_move_cutoffs + other.null_move_cutoffs,
            null_move_skips: self.null_move_skips + other.null_move_skips,
            hash_full_hits: self.hash_full_hits + other.hash_full_hits,
            hash_partial_hits: self.hash_partial_hits + other.hash_partial_hits,
            hash_misses: self.hash_misses + other.hash_misses,
            hash_move_cutoffs: self.hash_move_cutoffs + other.hash_move_cutoffs

        }
    }
}
<commit_msg>Use sjadam board monte carlo simulation<commit_after>mod uci;
pub mod board;
mod tests;
pub mod search_algorithms;
pub mod pgn;
pub mod uci_engine;

#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate itertools;
extern crate rand;
extern crate ordered_float;
extern crate rayon;
#[cfg(feature = "logging")]
extern crate log4rs;
#[cfg(feature = "profile")]
extern crate cpuprofiler;

use std::io;
use std::fmt;
use std::hash::Hash;
use std::time;

use search_algorithms::alpha_beta::Score;
use search_algorithms::mcts;
use search_algorithms::board::GameResult;
use pgn::PgnBoard;

use board::std_board::ChessBoard;
use board::crazyhouse_board::CrazyhouseBoard;
use board::sjadam_board::SjadamBoard;

#[cfg(feature = "logging")]
use log4rs::config::{Appender, Config, Root};
#[cfg(feature = "logging")]
use std::io::Write;

#[cfg(feature = "profile")]
use cpuprofiler::PROFILER;
use search_algorithms::board::Board;
use search_algorithms::board::ExtendedBoard;
use search_algorithms::alpha_beta::AlphaBeta;
use uci_engine::UciEngine;
use search_algorithms::monte_carlo::MonteCarlo;
use std::sync::Arc;
use std::sync::Mutex;
use search_algorithms::monte_carlo;

#[cfg(feature = "logging")]
fn init_log() -> Result<(), Box<std::error::Error>> {
    let appender = log4rs::append::file::FileAppender::builder().append(true).build("rasher.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("uci", Box::new(appender)))
        .build(Root::builder().appender("uci").build(log::LogLevelFilter::Debug))?;
    log4rs::init_config(config)?;
    Ok(())
}

fn main() {
    #[cfg(feature = "logging")]
    init_log().unwrap_or_else(|err| {
        let _ = writeln!(&mut io::stderr(), "Failed to open log file: {}", err);
    });

    #[cfg(feature = "profile")]
    PROFILER.lock().unwrap().start("./my-prof.profile").unwrap();

    let mut stdin = io::BufReader::new(io::stdin());
    info!("Opened log");

    for &cp in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0].iter() {
        let win_pct = monte_carlo::eval_to_win_pct(cp);
        println!("{} eval: {}", cp, win_pct);
        println!("{} win percentage: {} eval", win_pct, monte_carlo::win_pct_to_eval(win_pct))
    }

    let monte_carlo = MonteCarlo::init();
    let mut board = SjadamBoard::start_board();
    for uci_info in monte_carlo.search(board.clone(),
                                       uci::TimeRestriction::Infinite,
                                       Arc::new(Mutex::new(uci::EngineComm::new())),
                                       None) {
        println!("{}", uci_info.to_info_string(&mut board));
    }

    loop {
        if let Ok(input) = uci::get_engine_input(&mut stdin) {

	    let tokens : Vec<&str> = input.split_whitespace().collect();

            match tokens[0] {
                "uci" => {
                    uci::connect_engine(&mut stdin).unwrap();
                    return;
                },
                "isready" => {
                    info!("received isready from GUI");
                    uci::uci_send("readyok");
                },

                "mem_usage" => {
                    use std::mem;
                    println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                    println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                    println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                    println!("Sjadam board: {}", mem::size_of::<SjadamBoard>());
                    println!("Sjadam move: {}", mem::size_of::<board::sjadam_move::SjadamMove>());
                    println!("Board score: {}", mem::size_of::<Score>());
                    println!("Size of chess mcts node: {}",
                             mem::size_of::<mcts::MonteCarloTree<ChessBoard>>());

                },
                //"play_self" => play_game(&board::START_BOARD.clone()),
                "play_self" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_game(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_game(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_game(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "play" => {
                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let board = ChessBoard::start_board().clone();
                        play_human(board)
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let board = CrazyhouseBoard::start_board().clone();
                                play_human(board)
                            },
                            "sjadam" => {
                                let board = SjadamBoard::start_board().clone();
                                play_human(board)
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                },
                "mcts" => {

                    if tokens.len() == 1 || tokens[1] == "standard" {
                        let mut board = ChessBoard::start_board().clone();
                        search_algorithms::mcts::search_position(&mut board);
                    }
                    else {
                        match tokens[1] {
                            "crazyhouse" => {
                                let mut board = CrazyhouseBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            },
                            "sjadam" => {
                                let mut board = SjadamBoard::start_board().clone();
                                search_algorithms::mcts::search_position(&mut board);
                            }
                            s => println!("Unrecognized variant {}.", s),
                        }
                    }
                }
                "mcts_debug" => mcts::play_human(ChessBoard::start_board()),
                s => warn!("Unrecognized command \"{}\".", s),
            }
        }
        else {
            return;
        }
    }
}

/// Makes the engine play a game against itself
fn play_game<B> (mut board : B)
    where B: PgnBoard + ExtendedBoard + fmt::Debug + Send + 'static + Hash + Eq + Clone,
<B as Board>::Move: Sync + Send {
    println!("Board:\n{:?}", board);
    println!("\n");
    match board.game_result() {
        None => {
            let mut engine = AlphaBeta::init();
            let (score, mv) =
                engine.best_move(board.clone(),
                                 uci::TimeRestriction::MoveTime(time::Duration::from_secs(5)),
                                 None).unwrap();

            println!("Found move {:?} with score {}.", mv, score.uci_string(board.side_to_move()));
            board.do_move(mv);
            play_game(board);
        }
        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}
/// Play a game against the engine through stdin
fn play_human<B>(mut board : B)
    where B: 'static + PgnBoard + ExtendedBoard + fmt::Debug + Send + Hash + Eq,
          <B as Board>::Move: Sync + Send
{
    match board.game_result() {
        None => {
            use search_algorithms::board::Color::*;
            println!("Board:\n{:?}", board);
            // If black, play as human
            if board.side_to_move() == White {
                println!("Type your move as long algebraic notation (e2e4):");

                let reader = io::stdin();
                let mut input_str = "".to_string();
                let mut legal_moves = vec![];
                board.generate_moves(&mut legal_moves);
                // Loop until user enters a valid move
                loop {
                    input_str.clear();
                    reader.read_line(&mut input_str).expect("Failed to read line");

                    match board.move_from_lan(input_str.trim()) {
                        Ok(val) => {
                            if legal_moves.contains(&val) { break; }
                            println!("Move {:?} is illegal! Legal moves: {:?}", val, legal_moves);
                            println!("Try again: ");
                        }

                        Err(error) => {
                            println!("{}, try again.", error);
                        },
                    }
                }
                let c_move = board.move_from_lan(input_str.trim()).unwrap();
                board.do_move(c_move);
                play_human(board);
            }
            else {

                let mut engine = AlphaBeta::init();
                let (score, best_move) =
                    engine.best_move(board.clone(),
                                     uci::TimeRestriction::MoveTime(time::Duration::from_secs(5)),
                                     None).unwrap();

                println!("Computer played {:?} with score {}",
                         best_move, score.uci_string(board.side_to_move()));
                board.do_move(best_move);
                play_human(board);
            }
        }

        Some(GameResult::WhiteWin) => println!("White won at move! Board:\n{:?}", board),
        Some(GameResult::BlackWin) => println!("Black won! Board:\n{:?}", board),
        Some(GameResult::Draw) => println!("The game was drawn! Board:\n{:?}", board),
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern: u64,
    leaf: u64,
    qsearch: u64,
    hash_full_hits: u64, // TT hit causes a cutoff
    hash_partial_hits: u64, // TT hit, hash move is used, but no cutoff
    hash_misses: u64, // No TT hit
    hash_move_cutoffs: u64, // Hash move causes a cutoff before other moves are tried
    null_move_cutoffs: u64,
    null_move_skips: u64,
}

impl NodeCount {
    fn new() -> Self {
        NodeCount { intern: 0, leaf: 0, qsearch: 0, null_move_cutoffs: 0,
            null_move_skips: 0, hash_full_hits: 0,
            hash_partial_hits: 0, hash_misses: 0, hash_move_cutoffs: 0 }
    }
    fn total(&self) -> u64 {
        self.intern + self.leaf + self.qsearch
    }
}

impl std::ops::Add for NodeCount {
    type Output = NodeCount;
    fn add(self, other: NodeCount) -> Self {
        NodeCount {
            intern: self.intern + other.intern, leaf: self.leaf + other.leaf,
            qsearch: self.qsearch + other.qsearch,
            null_move_cutoffs: self.null_move_cutoffs + other.null_move_cutoffs,
            null_move_skips: self.null_move_skips + other.null_move_skips,
            hash_full_hits: self.hash_full_hits + other.hash_full_hits,
            hash_partial_hits: self.hash_partial_hits + other.hash_partial_hits,
            hash_misses: self.hash_misses + other.hash_misses,
            hash_move_cutoffs: self.hash_move_cutoffs + other.hash_move_cutoffs

        }
    }
}
<|endoftext|>


<commit_before>extern crate sonos_discovery;

use sonos_discovery::Discover;

fn main() {
    let discovery = Discover::new();
    let devices = discovery.start(None, None);
    for device in devices {
        println!("{}", device)
    }
}
<commit_msg>[main] Unwrap discovery result<commit_after>extern crate sonos_discovery;

use sonos_discovery::Discover;

fn main() {
    let discovery = Discover::new().unwrap();
    let devices = discovery.start(None, None);
    for device in devices {
        println!("{}", device)
    }
}
<|endoftext|>


<commit_before>#![crate_id = "rustic"]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;

use std::io::fs;
use std::io::process::{Command,ExitSignal,ExitStatus};
use std::os;

use tmpdir::TmpDir;

mod child;
mod rand;
mod tmpdir;

// FIXME Need a better way to detect which argument is the crate file
fn is_crate(arg: &str) -> bool {
    arg.ends_with(".rs")
}

fn main() {
    let args = os::args();
    let args = args.slice_from(1);

    // If `--run` is not in the arguments: pass all the arguments to `rustc`
    if args.iter().all(|arg| arg.as_slice() != "--run") {
        let mut cmd = Command::new("rustc");
        cmd.args(args);

        info!("cwd: . | cmd: `{}`", cmd);
        // FIXME Replace `output` for `spawn`, and redirect child std{out,err}
        match cmd.spawn() {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(p) => match child::supplant(p) {
                Err(e) => fail!("`{}` failed: {}", cmd, e),
                Ok(exit) => {
                    if !exit.success() {
                        let exit_code = match exit {
                            ExitSignal(code) => code,
                            ExitStatus(code) => code,
                        };

                        os::set_exit_status(exit_code);
                        return;
                    }
                },
            },
        }
    }

    // Separate the crate file from the other arguments
    let current_dir = os::getcwd();
    let crate_path = match args.iter().find(|arg| is_crate(arg.as_slice())) {
        Some(arg) => {
            let path = Path::new(arg.as_slice());
            if path.is_absolute() {
                path
            } else {
                current_dir.join(path)
            }
        }
        None => fail!("Didn't find a crate file in the arguments passed: {}",
                      args),
    };
    let args: Vec<&str> = args.iter().filter_map(|arg| {
        let arg = arg.as_slice();
        if is_crate(arg) { None } else { Some(arg) }
    }).collect();

    let tmpdir = TmpDir::new();
    let tmpdir_path = tmpdir.path();
    let tmpdir_display = tmpdir_path.display();

    // Before `--run`: arguments for the compiler
    let mut splitted_args = args.as_slice().split(|&flag| flag == "--run");
    let rustc_args = splitted_args.next().unwrap();

    // TODO `--crate-type=lib` should also be forbidden
    // XXX What if the crate has a `crate_type=*lib` attribute?
    if rustc_args.iter().any(|&flag| flag == "--out-dir") {
        fail!("Can't use both `--out-dir` and `--run` flags at the same time");
    }

    // Compile
    let mut cmd = Command::new("rustc");
    cmd.args(rustc_args).arg(crate_path);
    info!("cwd: {} | cmd: `{}`", tmpdir_display, cmd);
    // FIXME Replace `output` for `spawn`, and redirect child std{out,err}
    match cmd.cwd(tmpdir_path).spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => {
                if !exit.success() {
                    let exit_code = match exit {
                        ExitSignal(code) => code,
                        ExitStatus(code) => code,
                    };

                    os::set_exit_status(exit_code);
                    return;
                }
            },
        },
    }

    // Look for the produced binary
    let mut cmd = match fs::readdir(tmpdir_path) {
        Err(e) => fail!("`ls {}` failed: {}", tmpdir_display, e),
        Ok(paths) => match paths.as_slice().get(0) {
            Some(path) => Command::new(path),
            None => fail!("no binary found in {}", tmpdir_display),
        }
    };

    // After `--run`: arguments for the executable
    let executable_args = splitted_args.next().unwrap();
    cmd.args(executable_args);

    // Execute
    info!("cwd: . | cmd: `{}`", cmd);
    // FIXME Replace `output` for `spawn`, and redirect child std{out,err}
    match cmd.spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => {
                if !exit.success() {
                    let exit_code = match exit {
                        ExitSignal(code) => code,
                        ExitStatus(code) => code,
                    };

                    os::set_exit_status(exit_code);
                    return;
                }
            },
        }
    }
}
<commit_msg>Better detection of the crate file in the arguments. Closes #1<commit_after>#![crate_id = "rustic"]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;

use std::io::fs;
use std::io::process::{Command,ExitSignal,ExitStatus};
use std::os;

use tmpdir::TmpDir;

mod child;
mod rand;
mod tmpdir;

fn is_crate(arg: &str) -> bool {
    Path::new(arg).exists()
}

fn main() {
    let args = os::args();
    let args = args.slice_from(1);

    // If `--run` is not in the arguments: pass all the arguments to `rustc`
    if args.iter().all(|arg| arg.as_slice() != "--run") {
        let mut cmd = Command::new("rustc");
        cmd.args(args);

        info!("cwd: . | cmd: `{}`", cmd);
        match cmd.spawn() {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(p) => match child::supplant(p) {
                Err(e) => fail!("`{}` failed: {}", cmd, e),
                Ok(exit) => if !exit.success() {
                    let exit_code = match exit {
                        ExitSignal(code) => code,
                        ExitStatus(code) => code,
                    };

                    os::set_exit_status(exit_code);
                    return;
                },
            },
        }
    }

    // Before `--run`: arguments for the compiler
    let mut splitted_args = args.split(|arg| arg.as_slice() == "--run");
    let rustc_args = splitted_args.next().unwrap();
    let executable_args = splitted_args.next().unwrap();

    // TODO `--crate-type=lib` should also be forbidden
    // XXX What if the crate has a `crate_type=*lib` attribute?
    if rustc_args.iter().any(|arg| arg.as_slice() == "--out-dir") {
        fail!("Can't use both `--out-dir` and `--run` flags at the same time");
    }

    // Separate the crate file from the other arguments
    let crate_arg = match args.iter().find(|arg| {
        is_crate(arg.as_slice())
    }) {
        Some(arg) => arg,
        None => fail!("Didn't find a crate file in the arguments passed"),
    };

    // Use full path for the crate file
    let current_dir = os::getcwd();
    let crate_path = {
        let path = Path::new(crate_arg.as_slice());

        if path.is_absolute() {
            path
        } else {
            current_dir.join(path)
        }
    };

    // Build the rustc command
    let mut cmd = Command::new("rustc");
    for arg in rustc_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }
    cmd.arg(crate_path);

    // Create temporary directory
    let tmpdir = TmpDir::new();
    let tmpdir_path = tmpdir.path();
    let tmpdir_display = tmpdir_path.display();

    // Compile
    info!("cwd: {} | cmd: `{}`", tmpdir_display, cmd);
    match cmd.cwd(tmpdir_path).spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        },
    }

    // Look for the produced binary
    let mut cmd = match fs::readdir(tmpdir_path) {
        Err(e) => fail!("`ls {}` failed: {}", tmpdir_display, e),
        Ok(paths) => match paths.as_slice().get(0) {
            Some(path) => Command::new(path),
            None => fail!("no binary found in {}", tmpdir_display),
        }
    };

    // Build the executable command
    for arg in executable_args.iter().filter(|&arg| arg != crate_arg) {
        cmd.arg(arg.as_slice());
    }

    // Execute
    info!("cwd: . | cmd: `{}`", cmd);
    match cmd.spawn() {
        Err(e) => fail!("`{}` failed: {}", cmd, e),
        Ok(p) => match child::supplant(p) {
            Err(e) => fail!("`{}` failed: {}", cmd, e),
            Ok(exit) => if !exit.success() {
                let exit_code = match exit {
                    ExitSignal(code) => code,
                    ExitStatus(code) => code,
                };

                os::set_exit_status(exit_code);
                return;
            },
        }
    }
}
<|endoftext|>


<commit_before>#![feature(old_orphan_check)]
extern crate "graphviz" as dot;
extern crate glob;
extern crate "rustc-serialize" as rustc_serialize;

use std::borrow::IntoCow;
use std::collections::{HashMap, HashSet};
use std::io::{BufferedReader, File};

#[derive(RustcDecodable)]
#[allow(dead_code)]
struct CrateInfo {
    name: String,
    vers: String,
    deps: Vec<DepInfo>,
    cksum: String,
    features: HashMap<String, Vec<String>>,
    yanked: bool,
}

#[derive(RustcDecodable)]
#[allow(dead_code)]
struct DepInfo {
    name: String,
    req: String,
    features: Vec<String>,
    optional: bool,
    default_features: bool,
    target: Option<String>,
    kind: Option<String>
}

fn main() {
    let mut opts = glob::MatchOptions::new();
    opts.require_literal_leading_dot = true;

    let mut crates = vec![];
    let mut edges = vec![];
    let mut interacts = HashSet::new();

    for path in glob::glob_with("crates.io-index/*/*/*", &opts)
                    .chain(glob::glob_with("crates.io-index/[12]/*", &opts)) {
        let file = File::open(&path).unwrap();
        let last_line = BufferedReader::new(file).lines().last().unwrap().unwrap();
        let crate_info: CrateInfo = rustc_serialize::json::decode(&*last_line).unwrap();

        crates.push(crate_info.name.clone());
        if !crate_info.deps.is_empty() {
            interacts.insert(crate_info.name.clone());
        }
        let mut deps = crate_info.deps.iter()
            .filter(|d| d.kind.as_ref().map_or(true, |s| &**s != "dev"))
            .map(|d| &d.name)
            .collect::<Vec<_>>();

        deps.sort();
        deps.dedup();
        for &dep_name in deps.iter() {
            interacts.insert(dep_name.clone());
            edges.push((crate_info.name.clone(), dep_name.clone()));
        }
    }

    println!("total crates: {}", crates.len());
    crates.retain(|name| interacts.contains(name));
    println!("filtered crates: {}", crates.len());

    let graph = Graph {
        crates: crates,
        deps: edges,
    };
    dot::render(&graph, &mut std::io::stdout()).ok().expect("bad render");
}

struct Graph {
    crates: Vec<String>,
    deps: Vec<(String, String)>,
}

impl<'a> dot::Labeller<'a, String, (String, String)> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("cratesiodeps".to_string()).ok().expect("bad graph id")
    }

    fn node_id(&'a self, n: &String) -> dot::Id<'a> {
        dot::Id::new(n.replace("-", "_")).ok().expect("bad id")
    }

    fn node_label<'b>(&self, n: &String) -> dot::LabelText<'a> {
        dot::LabelStr(format!("{0}", n).into_cow())
    }
}

impl<'a> dot::GraphWalk<'a, String, (String, String)> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, String> {
        self.crates.clone().into_cow()
    }
    fn edges(&self) -> dot::Edges<'a, (String, String)> {
        self.deps.clone().into_cow()
    }

    fn source(&self, e: &(String, String)) -> String {
        e.0.clone()
    }
    fn target(&self, e: &(String, String)) -> String {
        e.1.clone()
    }
}
<commit_msg>Eliminate crates after removing dev deps from their dependencies.<commit_after>#![feature(old_orphan_check)]
extern crate "graphviz" as dot;
extern crate glob;
extern crate "rustc-serialize" as rustc_serialize;

use std::borrow::IntoCow;
use std::collections::{HashMap, HashSet};
use std::io::{BufferedReader, File};

#[derive(RustcDecodable)]
#[allow(dead_code)]
struct CrateInfo {
    name: String,
    vers: String,
    deps: Vec<DepInfo>,
    cksum: String,
    features: HashMap<String, Vec<String>>,
    yanked: bool,
}

#[derive(RustcDecodable)]
#[allow(dead_code)]
struct DepInfo {
    name: String,
    req: String,
    features: Vec<String>,
    optional: bool,
    default_features: bool,
    target: Option<String>,
    kind: Option<String>
}

fn main() {
    let mut opts = glob::MatchOptions::new();
    opts.require_literal_leading_dot = true;

    let mut crates = vec![];
    let mut edges = vec![];
    let mut interacts = HashSet::new();

    for path in glob::glob_with("crates.io-index/*/*/*", &opts)
                    .chain(glob::glob_with("crates.io-index/[12]/*", &opts)) {
        let file = File::open(&path).unwrap();
        let last_line = BufferedReader::new(file).lines().last().unwrap().unwrap();
        let crate_info: CrateInfo = rustc_serialize::json::decode(&*last_line).unwrap();

        crates.push(crate_info.name.clone());
        let mut deps = crate_info.deps.iter()
            .filter(|d| d.kind.as_ref().map_or(true, |s| &**s != "dev"))
            .map(|d| &d.name)
            .collect::<Vec<_>>();
        if !deps.is_empty() {
            interacts.insert(crate_info.name.clone());
        }

        deps.sort();
        deps.dedup();
        for &dep_name in deps.iter() {
            interacts.insert(dep_name.clone());
            edges.push((crate_info.name.clone(), dep_name.clone()));
        }
    }

    println!("total crates: {}", crates.len());
    crates.retain(|name| interacts.contains(name));
    println!("filtered crates: {}", crates.len());

    let graph = Graph {
        crates: crates,
        deps: edges,
    };
    dot::render(&graph, &mut std::io::stdout()).ok().expect("bad render");
}

struct Graph {
    crates: Vec<String>,
    deps: Vec<(String, String)>,
}

impl<'a> dot::Labeller<'a, String, (String, String)> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("cratesiodeps".to_string()).ok().expect("bad graph id")
    }

    fn node_id(&'a self, n: &String) -> dot::Id<'a> {
        dot::Id::new(n.replace("-", "_")).ok().expect("bad id")
    }

    fn node_label<'b>(&self, n: &String) -> dot::LabelText<'a> {
        dot::LabelStr(format!("{0}", n).into_cow())
    }
}

impl<'a> dot::GraphWalk<'a, String, (String, String)> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, String> {
        self.crates.clone().into_cow()
    }
    fn edges(&self) -> dot::Edges<'a, (String, String)> {
        self.deps.clone().into_cow()
    }

    fn source(&self, e: &(String, String)) -> String {
        e.0.clone()
    }
    fn target(&self, e: &(String, String)) -> String {
        e.1.clone()
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate elf;
extern crate rbpf;

use std::process;
use clap::{Arg, App};
use std::fs::File;
use std::io::Read;

fn main() {
    let matches = App::new("ebpf-disasm")
        .version("1.0")
        .author("Jan-Erik Rediger")
        .about("Output disassembled eBPF read from an ELF file")
        .arg(Arg::with_name("section")
             .short("s")
             .long("section")
             .value_name("SECTION")
             .help("Specify a section name")
             .takes_value(true))
        .arg(Arg::with_name("INPUT")
             .help("Sets the input ELF file to use")
             .required(true)
             .index(1))
        .arg(Arg::with_name("bytecode")
            .short("b")
            .long("bytecode")
            .help("Only show raw bytecode"))
        .arg(Arg::with_name("ccode")
            .short("c")
            .long("ccode")
            .help("Only show a C compatible bpf_insn array"))
        .arg(Arg::with_name("raw")
            .short("r")
            .long("raw")
            .help("Treat input as raw bytes"))
        .arg(Arg::with_name("list-sections")
            .short("l")
            .long("list")
            .help("List sections of the object file"))
        .arg(Arg::with_name("number")
            .short("n")
            .long("number")
            .help("Number all output lines"))
        .get_matches();

    let section = matches.value_of("section").unwrap_or(".classifier");
    let input_file = matches.value_of("INPUT").unwrap();
    let show_bytecode = matches.is_present("bytecode");
    let show_ccode = matches.is_present("ccode");
    let raw = matches.is_present("raw");
    let list_sections = matches.is_present("list-sections");
    let number = matches.is_present("number");

    if show_bytecode && show_ccode {
        println!("Can't show both bytecode and C code.");
        process::exit(1);
    }

    if raw && list_sections {
        println!("Can't list sections of raw bytecode.");
        process::exit(1);
    }

    let prog;
    if raw {
        let mut file = match File::open(&input_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Could not read file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        };

        let mut buf = Vec::new();
        match file.read_to_end(&mut buf) {
            Ok(_) => {},
            Err(e) => {
                println!("Could not read file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        }
        prog = buf;
    } else {
        let file = match elf::File::open_path(&input_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Could not read elf file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        };

        if list_sections {
            println!("Sections of {}:", input_file);
            for section in file.sections {
                println!("  {}", section.shdr.name);
            }
            process::exit(0);
        }

        let text_scn = match file.get_section(&section) {
            Some(s) => s,
            None => {
                println!("Failed to lookup '{}' section.", section);
                process::exit(1);
            }
        };

        prog = text_scn.data.clone();
    }

    if show_bytecode {
        for insn in prog.chunks(8) {
            for i in insn {
                print!("0x{:>02x}, ", i);
            }
            println!("");
        }
    } else if show_ccode {
        println!("struct bpf_insn prog[] = {{");
        for insn in prog.chunks(8) {
            print!("\t{{ ");
            print!(".code = 0x{:>02x}, ", insn[0]);
            print!(".dst_reg = 0x{:>x}, ", insn[1]&0x0f);
            print!(".src_reg = 0x{:>x}, ", insn[1]>>4);
            let off = (insn[3] as u16)<<8 | (insn[2] as u16);
            print!(".off = 0x{:>04x}, ", off);
            let imm = (insn[7] as u32)<<24 |
                (insn[6] as u32)<<16 |
                (insn[5] as u32)<<8 |
                (insn[4] as u32)<<0;
            print!(".imm = 0x{:>08x}", imm);
            println!(" }},");
        }
        println!("}};");
    } else {
        let mut idx = 0;
        for insn in rbpf::disassembler::to_insn_vec(&prog) {
            if number {
                print!("{:>6}:  ", idx);
            }
            print!("{}", insn.desc.replace(" ", "\t"));

            if is_jmp(&insn) {
                print!("\t(jump to {})", idx + insn.off as isize);
            }

            if is_wide_op(&insn) {
                idx += 1;
            }

            println!();
            idx += 1;
        }
    }
}

// Check if the instruction is a jump
// (but not a call, tailcall or exit)
//
// Jumps are relative from the current instruction pointer
fn is_jmp(insn: &rbpf::disassembler::HLInsn) -> bool {
    (insn.opc & 0x07) == rbpf::ebpf::BPF_JMP &&
        (insn.opc != rbpf::ebpf::CALL) &&
        (insn.opc != rbpf::ebpf::TAIL_CALL) &&
        (insn.opc != rbpf::ebpf::EXIT)
}

// Check if the instruction is spread across two instructions
//
// Currently only a wide load uses a second instruction for the u64 field.
fn is_wide_op(insn: &rbpf::disassembler::HLInsn) -> bool {
    insn.opc == rbpf::ebpf::LD_DW_IMM
}
<commit_msg>feat: Dump bytecode in bytes or hex<commit_after>extern crate clap;
extern crate elf;
extern crate rbpf;

use std::process;
use clap::{Arg, App};
use std::fs::File;
use std::io::Read;

fn main() {
    let matches = App::new("ebpf-disasm")
        .version("1.0")
        .author("Jan-Erik Rediger")
        .about("Output disassembled eBPF read from an ELF file")
        .arg(Arg::with_name("section")
             .short("s")
             .long("section")
             .value_name("SECTION")
             .help("Specify a section name")
             .takes_value(true))
        .arg(Arg::with_name("INPUT")
             .help("Sets the input ELF file to use")
             .required(true)
             .index(1))
        .arg(Arg::with_name("hex")
            .short("h")
            .long("hex")
            .help("Dump bytecode in hex"))
        .arg(Arg::with_name("bytecode")
            .short("b")
            .long("bytecode")
            .help("Dump raw bytecode"))
        .arg(Arg::with_name("ccode")
            .short("c")
            .long("ccode")
            .help("Only show a C compatible bpf_insn array"))
        .arg(Arg::with_name("raw")
            .short("r")
            .long("raw")
            .help("Treat input as raw bytes"))
        .arg(Arg::with_name("list-sections")
            .short("l")
            .long("list")
            .help("List sections of the object file"))
        .arg(Arg::with_name("number")
            .short("n")
            .long("number")
            .help("Number all output lines"))
        .get_matches();

    let section = matches.value_of("section").unwrap_or(".classifier");
    let input_file = matches.value_of("INPUT").unwrap();
    let show_hex = matches.is_present("hex");
    let show_bytecode = matches.is_present("bytecode");
    let show_ccode = matches.is_present("ccode");
    let raw = matches.is_present("raw");
    let list_sections = matches.is_present("list-sections");
    let number = matches.is_present("number");

    let number_formats = show_hex as u8 + show_bytecode as u8 + show_ccode as u8;

    if number_formats > 1 {
        println!("Can't dump multiple bytecode formats.");
        process::exit(1);
    }

    if raw && list_sections {
        println!("Can't list sections of raw bytecode.");
        process::exit(1);
    }

    let prog;
    if raw {
        let mut file = match File::open(&input_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Could not read file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        };

        let mut buf = Vec::new();
        match file.read_to_end(&mut buf) {
            Ok(_) => {},
            Err(e) => {
                println!("Could not read file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        }
        prog = buf;
    } else {
        let file = match elf::File::open_path(&input_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Could not read elf file.");
                println!("Error: {:?}", e);
                process::exit(1);
            }
        };

        if list_sections {
            println!("Sections of {}:", input_file);
            for section in file.sections {
                println!("  {}", section.shdr.name);
            }
            process::exit(0);
        }

        let text_scn = match file.get_section(&section) {
            Some(s) => s,
            None => {
                println!("Failed to lookup '{}' section.", section);
                process::exit(1);
            }
        };

        prog = text_scn.data.clone();
    }

    if show_hex {
        for insn in prog.chunks(8) {
            for i in insn {
                print!("0x{:>02x}, ", i);
            }
            println!("");
        }
    } else if show_ccode {
        println!("struct bpf_insn prog[] = {{");
        for insn in prog.chunks(8) {
            print!("\t{{ ");
            print!(".code = 0x{:>02x}, ", insn[0]);
            print!(".dst_reg = 0x{:>x}, ", insn[1]&0x0f);
            print!(".src_reg = 0x{:>x}, ", insn[1]>>4);
            let off = (insn[3] as u16)<<8 | (insn[2] as u16);
            print!(".off = 0x{:>04x}, ", off);
            let imm = (insn[7] as u32)<<24 |
                (insn[6] as u32)<<16 |
                (insn[5] as u32)<<8 |
                (insn[4] as u32)<<0;
            print!(".imm = 0x{:>08x}", imm);
            println!(" }},");
        }
        println!("}};");

    } else if show_bytecode {
        use std::io::{self, Write};
        let stdout = io::stdout();
        let mut handle = stdout.lock();
        handle.write_all(&prog).expect("Can't dump bytecode to stdout");
    } else {
        let mut idx = 0;
        for insn in rbpf::disassembler::to_insn_vec(&prog) {
            if number {
                print!("{:>6}:  ", idx);
            }
            print!("{}", insn.desc.replace(" ", "\t"));

            if is_jmp(&insn) {
                print!("\t(jump to {})", idx + insn.off as isize);
            }

            if is_wide_op(&insn) {
                idx += 1;
            }

            println!();
            idx += 1;
        }
    }
}

// Check if the instruction is a jump
// (but not a call, tailcall or exit)
//
// Jumps are relative from the current instruction pointer
fn is_jmp(insn: &rbpf::disassembler::HLInsn) -> bool {
    (insn.opc & 0x07) == rbpf::ebpf::BPF_JMP &&
        (insn.opc != rbpf::ebpf::CALL) &&
        (insn.opc != rbpf::ebpf::TAIL_CALL) &&
        (insn.opc != rbpf::ebpf::EXIT)
}

// Check if the instruction is spread across two instructions
//
// Currently only a wide load uses a second instruction for the u64 field.
fn is_wide_op(insn: &rbpf::disassembler::HLInsn) -> bool {
    insn.opc == rbpf::ebpf::LD_DW_IMM
}
<|endoftext|>


<commit_before>//! Preprocess and rearrange lines of input.
//!
//! Used for literate programming.

#[macro_use] extern crate error_chain;
extern crate regex;
extern crate docopt;
extern crate rustc_serialize;

mod parsing;
mod input;

mod errors {
  error_chain! {
    links {
      Parse(::parsing::errors::Error, ::parsing::errors::ErrorKind);
      Input(::input::errors::Error, ::input::errors::ErrorKind);
    }
  }
}

use errors::*;

use std::process;
use std::io::stderr;
use std::io::Write;
use std::io;

use std::collections::BTreeMap;
use std::cell::RefCell;
use std::rc::Rc;
use std::mem;
use std::iter::IntoIterator;

use docopt::Docopt;

static VERSION: &'static str = "0.1.0";
macro_rules! VERSION_INFO {
  () => { "\
kaiseki {}
copyright (c) 2017 William Yao <williamyaoh@gmail.com>
license BSD 3-Clause
no warranty, whether implied or not
" }
}
static USAGE: &'static str = "
kaiseki -- literate programming preprocessing

Usage:
    kaiseki [<files>...]
    kaiseki (--help | --version)

Options:
    -h, --help              Display this help message
    --version               Display version information

Tangles together all lines of code into a single file, which then
gets output to `stdout'. See kaiseki(1) for a description of the
literate programming syntax.
";

#[derive(RustcDecodable)]
struct CLIArgs {
  arg_files: Vec<String>
}

fn main() {
  let cli_parser = Docopt::new(USAGE).unwrap()
    .version(Some(VERSION.to_string()))
    .help(true);

  let cli_args: CLIArgs = cli_parser.decode().map_err(|err| match err {
    ::docopt::Error::Version(version) => {
      print!(VERSION_INFO!(), version);
      process::exit(0);
    },
    other => other.exit()
  }).unwrap();

  if let Err(ref e) = go(cli_args.arg_files) {
    writeln!(stderr(), "kaiseki: {}", e)
      .unwrap();

    for e in e.iter().skip(1) {
      writeln!(stderr(), "  caused by: {}", e)
        .unwrap();
    }

    process::exit(1);
  }
}

/// Each Block is a part of an input file, delineated by anchor tags.
struct Block {
  contents: Vec<String>,
  from: Rc<String>,
  line: usize
}

impl Block {
  fn new(from: Rc<String>, line: usize) -> Self {
    Block {
      contents: Vec::new(),
      from: from,
      line: line
    }
  }
}

enum Section {
  Normal(Block),
  Anchored {
    /// Stored in reverse order of appearance in output file
    before: Vec<Block>,
    /// Stored in normal order of appearance in output file
    after: Vec<Block>
  }
}

fn go(filepaths: Vec<String>) -> Result<()> {
  let files = input::open_files(filepaths)?;

  // Errors that we accumulate during processing. We output them at the end.
  let mut errors = Vec::new();

  let mut sections = Vec::new();
  let mut lookup: BTreeMap<String, Rc<RefCell<Section>>> = BTreeMap::new();

  enum State {
    Insert,
    Before(String),
    After(String)
  }

  for file in files {
    use std::io::{BufReader, BufRead};

    let filename = Rc::new(file.name);
    let lines = BufReader::new(file.contents).lines();
    let mut lines = lines
      .enumerate()
      .map(|(lineno, line)| (lineno+1, line))
      .peekable();

    let mut state = State::Insert;
    let mut last_block = Block::new(filename.clone(), 1);

    loop {
      let next_anchor = process_block_lines(&mut lines, &mut last_block, &mut errors);

      if last_block.contents.len() > 0 {
        match state {
          State::Insert => {
            let section = Section::Normal(last_block);
            let section = Rc::new(RefCell::new(section));

            sections.push(section);
          },
          State::After(label) => {
            match lookup.get_mut(&label) {
              None => {
                errors.push(format!("error: {}: anchor {} doesn't exist", filename, label));
                let section = Section::Normal(last_block);
                let section = Rc::new(RefCell::new(section));

                sections.push(section);
              },
              Some(section) => {
                let mut section_ref = section.borrow_mut();
                let section: &mut Section = &mut section_ref;

                if let &mut Section::Anchored { ref mut after, .. } = section {
                  after.push(last_block);
                }

                // No reason to worry if the section is a Normal. We don't
                // ever put that kind of section in `lookup'.
              }
            };
          },
          State::Before(label) => {
            match lookup.get_mut(&label) {
              None => {
                errors.push(format!("error: {}: anchor {} doesn't exist", filename, label));
                let section = Section::Normal(last_block);
                let section = Rc::new(RefCell::new(section));

                sections.push(section);
              }
              Some(section) => {
                let mut section_ref = section.borrow_mut();
                let section: &mut Section = &mut section_ref;

                if let &mut Section::Anchored { ref mut before, .. } = section {
                  before.push(last_block);
                }

                // No reason to worry if the section is a Normal. We don't
                // every put that kind of section in `lookup'.
              }
            };
          }
        };
      }

      match next_anchor {
        None => break,
        Some(anchor) => {
          use parsing::Anchor;

          match anchor {
            Anchor::Insert => state = State::Insert,
            Anchor::Before(label) => state = State::Before(label),
            Anchor::After(label) => state = State::After(label),
            Anchor::Label(label) => {
              let section = Section::Anchored {
                before: Vec::new(),
                after: Vec::new()
              };
              let section = Rc::new(RefCell::new(section));

              sections.push(section.clone());
              lookup.insert(label, section.clone());

              state = State::Insert;
            }
          };

          let lineno = match lines.peek() {
            None => 9999999,
            Some(&(lineno, ref _line)) => lineno
          };

          last_block = Block::new(filename.clone(), lineno);
        }
      };
    }
  }

  // So that the try_unwrap() below will succeed
  mem::drop(lookup);

  for section in sections {
    let section = Rc::try_unwrap(section);

    if let Err(_) = section {
      writeln!(stderr(), "what").unwrap();
    }

    if let Ok(section) = section {
      let section = section.into_inner();

      match section {
        Section::Normal(block) => for line in block.contents {
          println!("{}", line);
        },
        Section::Anchored { before, after } => {
          for block in before.into_iter().rev() {
            for line in block.contents {
              println!("{}", line);
            }
          }

          for block in after.into_iter() {
            for line in block.contents {
              println!("{}", line);
            }
          }
        }
      };
    }
  }

  for error in errors {
    writeln!(stderr(), "{}", error)
      .unwrap();
  }

  Ok(())
}

/// Each block will end in either an anchor tag, or the end of the file.
/// Since our caller needs to handle the anchor tag, we pass it back.
fn process_block_lines<I>(lines: &mut I, block: &mut Block, errors: &mut Vec<String>) -> Option<parsing::Anchor> where
  I: Iterator<Item=(usize, std::result::Result<String, io::Error>)>
{
  for (lineno, line) in lines {
    match line {
      Err(err) => errors.push(format!("error: {}, line {}: {}", block.from, lineno, err)),
      Ok(line) => {
        let is_normal_line;

        match parsing::might_be_anchor(&line) {
          None => is_normal_line = true,
          Some(anchor) => {
            let text = anchor.as_str();
            let anchor = parsing::parse(text);

            match anchor {
              Ok(anchor) => {
                return Some(anchor);
              },
              Err(_) => {
                errors.push(format!("warn: {}, line {}: ignoring something that looks like an anchor: {}", block.from, lineno, text));
                is_normal_line = true;
              }
            };
          }
        };

        if is_normal_line {
          block.contents.push(line);
        }
      }
    }
  }

  None
}
<commit_msg>refactor out outputting<commit_after>//! Preprocess and rearrange lines of input.
//!
//! Used for literate programming.

#[macro_use] extern crate error_chain;
extern crate regex;
extern crate docopt;
extern crate rustc_serialize;

mod parsing;
mod input;

mod errors {
  error_chain! {
    links {
      Parse(::parsing::errors::Error, ::parsing::errors::ErrorKind);
      Input(::input::errors::Error, ::input::errors::ErrorKind);
    }
  }
}

use errors::*;

use std::process;
use std::io::{stdout, stderr};
use std::io::Write;
use std::io;

use std::collections::BTreeMap;
use std::cell::RefCell;
use std::rc::Rc;
use std::iter::IntoIterator;

use docopt::Docopt;

static VERSION: &'static str = "0.1.0";
macro_rules! VERSION_INFO {
  () => { "\
kaiseki {}
copyright (c) 2017 William Yao <williamyaoh@gmail.com>
license BSD 3-Clause
no warranty, whether implied or not
" }
}
static USAGE: &'static str = "
kaiseki -- literate programming preprocessing

Usage:
    kaiseki [<files>...]
    kaiseki (--help | --version)

Options:
    -h, --help              Display this help message
    --version               Display version information

Tangles together all lines of code into a single file, which then
gets output to `stdout'. See kaiseki(1) for a description of the
literate programming syntax.
";

#[derive(RustcDecodable)]
struct CLIArgs {
  arg_files: Vec<String>
}

fn main() {
  let cli_parser = Docopt::new(USAGE).unwrap()
    .version(Some(VERSION.to_string()))
    .help(true);

  let cli_args: CLIArgs = cli_parser.decode().map_err(|err| match err {
    ::docopt::Error::Version(version) => {
      print!(VERSION_INFO!(), version);
      process::exit(0);
    },
    other => other.exit()
  }).unwrap();

  if let Err(ref e) = go(cli_args.arg_files) {
    writeln!(stderr(), "kaiseki: {}", e)
      .unwrap();

    for e in e.iter().skip(1) {
      writeln!(stderr(), "  caused by: {}", e)
        .unwrap();
    }

    process::exit(1);
  }
}

/// Each Block is a part of an input file, delineated by anchor tags.
struct Block {
  contents: Vec<String>,
  from: Rc<String>,
  line: usize
}

impl Block {
  fn new(from: Rc<String>, line: usize) -> Self {
    Block {
      contents: Vec::new(),
      from: from,
      line: line
    }
  }
}

enum Section {
  Normal(Block),
  Anchored {
    /// Stored in reverse order of appearance in output file
    before: Vec<Block>,
    /// Stored in normal order of appearance in output file
    after: Vec<Block>
  }
}

fn go(filepaths: Vec<String>) -> Result<()> {
  let files = input::open_files(filepaths)?;

  // Errors that we accumulate during processing. We output them at the end.
  let mut errors = Vec::new();

  let mut sections = Vec::new();

  {
    let mut lookup: BTreeMap<String, Rc<RefCell<Section>>> = BTreeMap::new();

    enum State {
      Insert,
      Before(String),
      After(String)
    }

    for file in files {
      use std::io::{BufReader, BufRead};

      let filename = Rc::new(file.name);
      let lines = BufReader::new(file.contents).lines();
      let mut lines = lines
        .enumerate()
        .map(|(lineno, line)| (lineno+1, line))
        .peekable();

      let mut state = State::Insert;
      let mut last_block = Block::new(filename.clone(), 1);

      loop {
        let next_anchor = process_block_lines(&mut lines, &mut last_block, &mut errors);

        if last_block.contents.len() > 0 {
          match state {
            State::Insert => {
              let section = Section::Normal(last_block);
              let section = Rc::new(RefCell::new(section));

              sections.push(section);
            },
            State::After(label) => {
              match lookup.get_mut(&label) {
                None => {
                  errors.push(format!("error: {}: anchor {} doesn't exist", filename, label));
                  let section = Section::Normal(last_block);
                  let section = Rc::new(RefCell::new(section));

                  sections.push(section);
                },
                Some(section) => {
                  let mut section_ref = section.borrow_mut();
                  let section: &mut Section = &mut section_ref;

                  if let &mut Section::Anchored { ref mut after, .. } = section {
                    after.push(last_block);
                  }

                  // No reason to worry if the section is a Normal. We don't
                  // ever put that kind of section in `lookup'.
                }
              };
            },
            State::Before(label) => {
              match lookup.get_mut(&label) {
                None => {
                  errors.push(format!("error: {}: anchor {} doesn't exist", filename, label));
                  let section = Section::Normal(last_block);
                  let section = Rc::new(RefCell::new(section));

                  sections.push(section);
                }
                Some(section) => {
                  let mut section_ref = section.borrow_mut();
                  let section: &mut Section = &mut section_ref;

                  if let &mut Section::Anchored { ref mut before, .. } = section {
                    before.push(last_block);
                  }

                  // No reason to worry if the section is a Normal. We don't
                  // every put that kind of section in `lookup'.
                }
              };
            }
          };
        }

        match next_anchor {
          None => break,
          Some(anchor) => {
            use parsing::Anchor;

            match anchor {
              Anchor::Insert => state = State::Insert,
              Anchor::Before(label) => state = State::Before(label),
              Anchor::After(label) => state = State::After(label),
              Anchor::Label(label) => {
                let section = Section::Anchored {
                  before: Vec::new(),
                  after: Vec::new()
                };
                let section = Rc::new(RefCell::new(section));

                sections.push(section.clone());
                lookup.insert(label, section.clone());

                state = State::Insert;
              }
            };

            let lineno = match lines.peek() {
              None => 9999999,
              Some(&(lineno, ref _line)) => lineno
            };

            last_block = Block::new(filename.clone(), lineno);
          }
        };
      }
    }
  }

  let sections = {
    let mut result = Vec::new();

    for section in sections {
      let section = Rc::try_unwrap(section).map_err(|_| {
        "invariant failed: section had more than one strong reference"
      }).unwrap();
      let section = section.into_inner();

      result.push(section);
    }

    result
  };
  let out: Box<Write> = Box::new(stdout());

  print_processed_output(sections, out)?;

  for error in errors {
    writeln!(stderr(), "{}", error)
      .unwrap();
  }

  Ok(())
}

/// Each block will end in either an anchor tag, or the end of the file.
/// Since our caller needs to handle the anchor tag, we pass it back.
fn process_block_lines<I>(lines: &mut I, block: &mut Block, errors: &mut Vec<String>) -> Option<parsing::Anchor> where
  I: Iterator<Item=(usize, std::result::Result<String, io::Error>)>
{
  for (lineno, line) in lines {
    match line {
      Err(err) => errors.push(format!("error: {}, line {}: {}", block.from, lineno, err)),
      Ok(line) => {
        let is_normal_line;

        match parsing::might_be_anchor(&line) {
          None => is_normal_line = true,
          Some(anchor) => {
            let text = anchor.as_str();
            let anchor = parsing::parse(text);

            match anchor {
              Ok(anchor) => {
                return Some(anchor);
              },
              Err(_) => {
                errors.push(format!("warn: {}, line {}: ignoring something that looks like an anchor: {}", block.from, lineno, text));
                is_normal_line = true;
              }
            };
          }
        };

        if is_normal_line {
          block.contents.push(line);
        }
      }
    }
  }

  None
}

/// Print all the lines in the order that they should be.
fn print_processed_output(sections: Vec<Section>, mut out: Box<Write>) -> Result<()> {
  macro_rules! try_output {
    ($format:expr, $($arg:expr),*) => {
      writeln!(out, $format, $($arg),*).chain_err(|| "failed to write output")?
    }
  }

  for section in sections {
    match section {
      Section::Normal(block) => for line in block.contents {
        try_output!("{}", line);
      },
      Section::Anchored { before, after } => {
        for block in before.into_iter().rev() {
          for line in block.contents {
            try_output!("{}", line);
          }
        }
        for block in after.into_iter() {
          for line in block.contents {
            try_output!("{}", line);
          }
        }
      }
    };
  }

  Ok(())
}
<|endoftext|>