<commit_before>#![allow(unused_variables)]
#[allow(unused_doc_comment)]

extern crate env_logger;
extern crate getopts;
extern crate log;
extern crate failure;
extern crate mdblog;

use std::env;

use getopts::{HasArg, Matches, Occur, Options};
use failure::Fail;

use mdblog::{Mdblog, Result};

fn print_usage_and_exit(opts: &Options, exit_code: i32) -> ! {
    let brief = "\
Usage:
    mdblog init <blog>
    mdblog build
    mdblog server [-p <port>]  # unimplemented
    mdblog -v | --version
    mdblog -h | --help\
";
    if exit_code == 0 {
        eprint!("{}", opts.usage(brief));
    } else {
        print!("{}", opts.usage(brief));
    }
    ::std::process::exit(exit_code);
}

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "Display this message");
    opts.optflag("v", "version", "Print version info and exit");
    opts.opt("p",
             "port",
             "Server with port number",
             "<port>",
             HasArg::Yes,
             Occur::Optional);

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(why) => {
            eprintln!("{}", why);
            eprintln!("run `mdblog -h` to get the usage.");
            ::std::process::exit(1);
        },
    };

    if matches.opt_present("h") {
        print_usage_and_exit(&opts, 0);
    } else if matches.opt_present("v") {
        println!("mdblog {}", env!("CARGO_PKG_VERSION"));
        ::std::process::exit(0);
    } else if matches.free.len() < 1 {
        print_usage_and_exit(&opts, 2);
    }

    let res = match matches.free[0].as_ref() {
        "init" => init(&matches),
        "build" => build(&matches),
        "server" => server(&matches),
        _ => print_usage_and_exit(&opts, 3),
    };

    if let Err(ref e) = res {
        eprintln!("error: {}", e);

        for cause in e.causes() {
            eprintln!("{}", cause);
        }

        if let Some(backtrace) = e.backtrace() {
            eprintln!("backtrace: {:?}", backtrace);
        }
        ::std::process::exit(1);
    }
}

fn init(matches: &Matches) -> Result<()> {
    if matches.free.len() != 2 {
        panic!("`init` subcommand requires one argument.");
    }
    let dir = env::current_dir().unwrap().join(&matches.free[1]);
    let mut mb = Mdblog::new(dir).unwrap();
    mb.init().unwrap();
    Ok(())
}

fn build(matches: &Matches) -> Result<()> {
    let root_dir = env::current_dir().unwrap();
    let mut mb = Mdblog::new(&root_dir).unwrap();
    mb.load().unwrap();
    mb.build().unwrap();
    Ok(())
}

fn server(matches: &Matches) -> Result<()> {
    println!("server command");
    Ok(())
}
<commit_msg>enable lint unused doc comment<commit_after>#![allow(unused_variables)]

extern crate env_logger;
extern crate getopts;
extern crate log;
extern crate failure;
extern crate mdblog;

use std::env;

use getopts::{HasArg, Matches, Occur, Options};
use failure::Fail;

use mdblog::{Mdblog, Result};

fn print_usage_and_exit(opts: &Options, exit_code: i32) -> ! {
    let brief = "\
Usage:
    mdblog init <blog>
    mdblog build
    mdblog server [-p <port>]  # unimplemented
    mdblog -v | --version
    mdblog -h | --help\
";
    if exit_code == 0 {
        eprint!("{}", opts.usage(brief));
    } else {
        print!("{}", opts.usage(brief));
    }
    ::std::process::exit(exit_code);
}

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();

    let mut opts = Options::new();
    opts.optflag("h", "help", "Display this message");
    opts.optflag("v", "version", "Print version info and exit");
    opts.opt("p",
             "port",
             "Server with port number",
             "<port>",
             HasArg::Yes,
             Occur::Optional);

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(why) => {
            eprintln!("{}", why);
            eprintln!("run `mdblog -h` to get the usage.");
            ::std::process::exit(1);
        },
    };

    if matches.opt_present("h") {
        print_usage_and_exit(&opts, 0);
    } else if matches.opt_present("v") {
        println!("mdblog {}", env!("CARGO_PKG_VERSION"));
        ::std::process::exit(0);
    } else if matches.free.len() < 1 {
        print_usage_and_exit(&opts, 2);
    }

    let res = match matches.free[0].as_ref() {
        "init" => init(&matches),
        "build" => build(&matches),
        "server" => server(&matches),
        _ => print_usage_and_exit(&opts, 3),
    };

    if let Err(ref e) = res {
        eprintln!("error: {}", e);

        for cause in e.causes() {
            eprintln!("{}", cause);
        }

        if let Some(backtrace) = e.backtrace() {
            eprintln!("backtrace: {:?}", backtrace);
        }
        ::std::process::exit(1);
    }
}

fn init(matches: &Matches) -> Result<()> {
    if matches.free.len() != 2 {
        panic!("`init` subcommand requires one argument.");
    }
    let dir = env::current_dir().unwrap().join(&matches.free[1]);
    let mut mb = Mdblog::new(dir).unwrap();
    mb.init().unwrap();
    Ok(())
}

fn build(matches: &Matches) -> Result<()> {
    let root_dir = env::current_dir().unwrap();
    let mut mb = Mdblog::new(&root_dir).unwrap();
    mb.load().unwrap();
    mb.build().unwrap();
    Ok(())
}

fn server(matches: &Matches) -> Result<()> {
    println!("server command");
    Ok(())
}
<|endoftext|>


<commit_before>extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate docopt;

use docopt::Docopt;

use std::thread;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_shared::*;
use timely::example_shared::operators::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

mod typedrw;
mod graphmap;
use graphmap::GraphMMap;

mod sorting;
use sorting::{SegmentList, radix_sort_32};

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      file containing list of host:port for workers
";

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_spawn(communicators, source);
    }
    else if workers > 1 { pagerank_spawn(ProcessCommunicator::new_vector(workers), source); }
    else { pagerank_spawn(vec![ThreadCommunicator], source); };
}

fn pagerank_spawn<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank_thread(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

// returns [src/peers] degrees, (dst, deg) pairs, and a list of [src/peers] endpoints
fn transpose(mut edges: Vec<Vec<(u32, u32)>>, peers: usize, nodes: usize) -> (Vec<u32>, Vec<(u32, u32)>, Vec<u32>)  {

    let mut deg = vec![0; (nodes as usize / peers) + 1];
    for list in &edges {
        for &(s, _) in list {
            deg[s as usize / peers] += 1;
        }
    }

    // let start = time::precise_time_s();
    radix_sort_32(&mut edges, &mut Vec::new(), &|&(_,d)| d);
    // println!("sorted in {}s", time::precise_time_s() - start);

    let mut rev = Vec::<(u32,u32)>::with_capacity(deg.len());
    let mut trn = Vec::with_capacity(edges.len() * 1024);
    for list in edges {
        for (s,d) in list {
            let len = rev.len();
            if (len == 0) || (rev[len-1].0 < d) {
                rev.push((d, 0u32));
            }

            let len = rev.len();
            rev[len-1].1 += 1;
            trn.push(s / peers as u32);
        }
    }

    return (deg, rev, trn);
}

// pagerank dataflow graph has a set of edges as input, and a binary vertex that for each epoch of
// received edges initiates an iterative subcomputation to compute the pagerank.

fn pagerank_thread<C>(communicator: C, filename: String, _workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    let start = time::precise_time_s();
    let mut going = start;

    let graph = GraphMMap::new(&filename);
    let nodes = graph.nodes();

    let mut segments = SegmentList::new(1024); // list of edge segments

    let mut src = vec![];   // holds ranks
    let mut deg = vec![];   // holds source degrees
    let mut rev = vec![];   // holds (dst, deg) pairs
    let mut trn = vec![];   // holds transposed sources

    let mut input = root.subcomputation(|builder| {

        let (input, edges) = builder.new_input::<(u32, u32)>();
        let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

        edges.binary_notify(&ranks,
                            Exchange::new(|x: &(u32,u32)| x.0 as u64),
                            Exchange::new(|x: &(u32,f32)| x.0 as u64),
                            format!("pagerank"),
                            vec![RootTimestamp::new(0)],
                            move |input1, input2, output, notificator| {

            // receive incoming edges
            while let Some((_index, data)) = input1.pull() {
                segments.push(data.drain_temp());
            }

            while let Some((iter, _)) = notificator.next() {
                // if the very first iteration, prepare some stuff
                if iter.inner == 0 {
                    // println!("received edges in {}s", time::precise_time_s() - start);
                    // (deg, rev, trn) = transpose(srcs, dsts, peers);
                    let (a, b, c) = transpose(segments.finalize(), peers, nodes);
                    deg = a;
                    rev = b;
                    trn = c;
                    src = vec![0.0f32; deg.len()];
                }

                if iter.inner == 10 { going = time::precise_time_s(); }
                if iter.inner == 20 && index == 0 { println!("avg: {}", (time::precise_time_s() - going) / 10.0); }

                for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }

                let mut trn_slice = &trn[..];
                let mut rev_slice = &rev[..];
                while rev_slice.len() > 0 {
                    // TODO: session should just flush...
                    let mut session = output.session(&iter);
                    let next = std::cmp::min(100_000, rev_slice.len());
                    for &(dst, deg) in &rev_slice[..next] {
                        let mut accum = 0.0;
                        for &s in &trn_slice[..deg as usize] {
                            accum += src[s as usize];
                        }
                        trn_slice = &trn_slice[deg as usize..];
                        session.give((dst, accum));
                    }
                    rev_slice = &rev_slice[next..];
                }

                for s in &mut src { *s = 0.0; }
            }

            while let Some((iter, data)) = input2.pull() {
                notificator.notify_at(&iter);
                for (node, rank) in data.drain_temp() {
                    src[node as usize / peers] += rank;
                }
            }
        })
        .connect_loop(cycle);

        input
    });

    let mut edges = Vec::new();
    for node in 0..graph.nodes() {
        if node % peers == index {
            for dst in graph.edges(node) {
                edges.push((node as u32, *dst as u32));
            }
            if edges.len() > 1024 {
                input.send_at(0, edges.drain_temp());
                root.step();
            }
        }
    }

    input.send_at(0, edges.drain_temp());
    input.close();
    while root.step() { }

    if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
}

    // .unary_notify(
    //     Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
    //     format!("Aggregation"),
    //     vec![],
    //     move |input, output, iterator| {
    //         while let Some((iter, data)) = input.pull() {
    //             iterator.notify_at(&iter);
    //             for (node, rank) in data.drain_temp() {
    //                 acc[node as usize / workers] += rank;
    //             }
    //         }
    //
    //         while let Some((item, _)) = iterator.next() {
    //
    //             output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
    //                                      .map(|(u,f)| (((u * workers + local_index) as u32), f)));
    //
    //             for _ in 0..(1 + (nodes/workers)) { acc.push(0.0); }
    //             assert!(acc.len() == (1 + (nodes/workers)));
    //         }
    //     }
    // )
<commit_msg>prints topology information<commit_after>extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate docopt;

use docopt::Docopt;

use std::thread;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_shared::*;
use timely::example_shared::operators::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

mod typedrw;
mod graphmap;
use graphmap::GraphMMap;

mod sorting;
use sorting::{SegmentList, radix_sort_32};

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      file containing list of host:port for workers
";

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_spawn(communicators, source);
    }
    else if workers > 1 { pagerank_spawn(ProcessCommunicator::new_vector(workers), source); }
    else { pagerank_spawn(vec![ThreadCommunicator], source); };
}

fn pagerank_spawn<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank_thread(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

// returns [src/peers] degrees, (dst, deg) pairs, and a list of [src/peers] endpoints
fn transpose(mut edges: Vec<Vec<(u32, u32)>>, peers: usize, nodes: usize) -> (Vec<u32>, Vec<(u32, u32)>, Vec<u32>)  {

    let mut deg = vec![0; (nodes as usize / peers) + 1];
    for list in &edges {
        for &(s, _) in list {
            deg[s as usize / peers] += 1;
        }
    }

    // let start = time::precise_time_s();
    radix_sort_32(&mut edges, &mut Vec::new(), &|&(_,d)| d);
    // println!("sorted in {}s", time::precise_time_s() - start);

    let mut rev = Vec::<(u32,u32)>::with_capacity(deg.len());
    let mut trn = Vec::with_capacity(edges.len() * 1024);
    for list in edges {
        for (s,d) in list {
            let len = rev.len();
            if (len == 0) || (rev[len-1].0 < d) {
                rev.push((d, 0u32));
            }

            let len = rev.len();
            rev[len-1].1 += 1;
            trn.push(s / peers as u32);
        }
    }

    return (deg, rev, trn);
}

// pagerank dataflow graph has a set of edges as input, and a binary vertex that for each epoch of
// received edges initiates an iterative subcomputation to compute the pagerank.

fn pagerank_thread<C>(communicator: C, filename: String, _workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    let start = time::precise_time_s();
    let mut going = start;

    let graph = GraphMMap::new(&filename);
    let nodes = graph.nodes();

    let mut segments = SegmentList::new(1024); // list of edge segments

    let mut src = vec![];   // holds ranks
    let mut deg = vec![];   // holds source degrees
    let mut rev = vec![];   // holds (dst, deg) pairs
    let mut trn = vec![];   // holds transposed sources

    let mut input = root.subcomputation(|builder| {

        let (input, edges) = builder.new_input::<(u32, u32)>();
        let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

        edges.binary_notify(&ranks,
                            Exchange::new(|x: &(u32,u32)| x.0 as u64),
                            Exchange::new(|x: &(u32,f32)| x.0 as u64),
                            format!("pagerank"),
                            vec![RootTimestamp::new(0)],
                            move |input1, input2, output, notificator| {

            // receive incoming edges
            while let Some((_index, data)) = input1.pull() {
                segments.push(data.drain_temp());
            }

            while let Some((iter, _)) = notificator.next() {
                // if the very first iteration, prepare some stuff
                if iter.inner == 0 {
                    // println!("received edges in {}s", time::precise_time_s() - start);
                    // (deg, rev, trn) = transpose(srcs, dsts, peers);
                    let (a, b, c) = transpose(segments.finalize(), peers, nodes);
                    deg = a;
                    rev = b;
                    trn = c;
                    src = vec![0.0f32; deg.len()];
                    if index == 0 { println!("transformed; {} x {} shape", deg.len(), rev.len()); }
                }

                if iter.inner == 10 { going = time::precise_time_s(); }
                if iter.inner == 20 && index == 0 { println!("avg: {}", (time::precise_time_s() - going) / 10.0); }

                for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }

                let mut trn_slice = &trn[..];
                let mut rev_slice = &rev[..];
                while rev_slice.len() > 0 {
                    // TODO: session should just flush...
                    let mut session = output.session(&iter);
                    let next = std::cmp::min(100_000, rev_slice.len());
                    for &(dst, deg) in &rev_slice[..next] {
                        let mut accum = 0.0;
                        for &s in &trn_slice[..deg as usize] {
                            accum += src[s as usize];
                        }
                        trn_slice = &trn_slice[deg as usize..];
                        session.give((dst, accum));
                    }
                    rev_slice = &rev_slice[next..];
                }

                for s in &mut src { *s = 0.0; }
            }

            while let Some((iter, data)) = input2.pull() {
                notificator.notify_at(&iter);
                for (node, rank) in data.drain_temp() {
                    src[node as usize / peers] += rank;
                }
            }
        })
        .connect_loop(cycle);

        input
    });

    let mut edges = Vec::new();
    for node in 0..graph.nodes() {
        if node % peers == index {
            for dst in graph.edges(node) {
                edges.push((node as u32, *dst as u32));
            }
            if edges.len() > 1024 {
                input.send_at(0, edges.drain_temp());
                root.step();
            }
        }
    }

    input.send_at(0, edges.drain_temp());
    input.close();
    while root.step() { }

    if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
}

    // .unary_notify(
    //     Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
    //     format!("Aggregation"),
    //     vec![],
    //     move |input, output, iterator| {
    //         while let Some((iter, data)) = input.pull() {
    //             iterator.notify_at(&iter);
    //             for (node, rank) in data.drain_temp() {
    //                 acc[node as usize / workers] += rank;
    //             }
    //         }
    //
    //         while let Some((item, _)) = iterator.next() {
    //
    //             output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
    //                                      .map(|(u,f)| (((u * workers + local_index) as u32), f)));
    //
    //             for _ in 0..(1 + (nodes/workers)) { acc.push(0.0); }
    //             assert!(acc.len() == (1 + (nodes/workers)));
    //         }
    //     }
    // )
<|endoftext|>


<commit_before>use std::env;
use std::fmt;
use std::io;
use std::io::Write;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> String {
    match ret {
        &FunctionRetTy::Default => "void".to_string(),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty).into(),
    }
}

fn map_pat(pat: &Pat) -> String {
    match pat {
        &Pat::Ident(_, ref ident, _) => ident.to_string(),
        _ => format!("unknown {:?}", pat),
    }

}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(ref pat, ref ty) => format!("{} {}", map_ty(ty), map_pat(pat)),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.prefix);
    ret.push(' ');
    ret.push_str(&f.ident.as_ref().expect("Struct fields must have idents").to_string());
    ret.push_str(&converted.postfix);
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

fn main() {
    let p = env::args().nth(1).unwrap();

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        println!("WR_INLINE {}\n{}({})\n{};\n",
                                 map_return_type(&decl.output),
                                 item.ident,
                                 decl.inputs
                                     .iter()
                                     .map(map_arg)
                                     .collect::<Vec<_>>()
                                     .join(", "),
                                 wr_func_body(&item.attrs));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if !generics.ty_params.is_empty() {
                            println!("template<{}>",
                                     generics.ty_params
                                         .iter()
                                         .map(map_generic_param)
                                         .collect::<Vec<_>>()
                                         .join(", "));
                        }
                        if let &VariantData::Struct(ref fields) = variant {
                            println!("struct {} {{\n{}}};\n",
                                     item.ident,
                                     fields
                                         .iter()
                                         .map(map_field)
                                         .collect::<String>());
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        println!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                 item.ident,
                                 variants
                                     .iter()
                                     .fold((String::new(), -1), fold_enum_variants)
                                     .0);
                    }
                }
                _ => {}
            }
        }
    });
}
<commit_msg>Instead of printing conversions directly to stdout, stash them in a result struct. This is needed for selectively printing just the things we need<commit_after>use std::collections::HashMap;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> String {
    match ret {
        &FunctionRetTy::Default => "void".to_string(),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty).into(),
    }
}

fn map_pat(pat: &Pat) -> String {
    match pat {
        &Pat::Ident(_, ref ident, _) => ident.to_string(),
        _ => format!("unknown {:?}", pat),
    }

}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(ref pat, ref ty) => format!("{} {}", map_ty(ty), map_pat(pat)),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.prefix);
    ret.push(' ');
    ret.push_str(&f.ident.as_ref().expect("Struct fields must have idents").to_string());
    ret.push_str(&converted.postfix);
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<String>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type, and the value is the C code.
    c_ds: HashMap<String, String>,
    /// This holds the dependency tree. If a struct contains another struct
    /// or an enum it will be in this map. The key is the dependent type,
    /// the list of values are all the things it depends on.
    _dep_tree: HashMap<String, Vec<String>>,
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        c_ds: HashMap::new(),
        _dep_tree: HashMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        results.lock().unwrap().funcs.push(
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    map_return_type(&decl.output),
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(map_arg)
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs)));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut c_struct = String::new();
                            if !generics.ty_params.is_empty() {
                                c_struct.push_str(
                                    &(format!("template<{}>",
                                         generics.ty_params
                                             .iter()
                                             .map(map_generic_param)
                                             .collect::<Vec<_>>()
                                             .join(", "))));
                            }
                            c_struct.push_str(
                                &(format!("struct {} {{\n{}}};\n",
                                     item.ident,
                                     fields
                                         .iter()
                                         .map(map_field)
                                         .collect::<String>())));
                            results.lock().unwrap().c_ds.insert(
                                item.ident.to_string(),
                                c_struct);
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_enum = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                 item.ident,
                                 variants
                                     .iter()
                                     .fold((String::new(), -1), fold_enum_variants)
                                     .0);
                        results.lock().unwrap().c_ds.insert(
                            item.ident.to_string(),
                            c_enum);
                    }
                }
                _ => {}
            }
        }
    });
    for (_, c_stuff) in &results.lock().unwrap().c_ds {
        println!("{}", c_stuff);
    }
    for func in &results.lock().unwrap().funcs {
        println!("{}", func);
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>work in progress<commit_after>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#![allow(non_snake_case)]

extern crate libc;

mod forker;
mod logger;

use forker::hello;
//use forker::hello;
//use libc;
//use libc::fork;
//use libc::exit;

fn main() {
    println!("Hello, world!");
    hello();

    let x: i32;
    unsafe {
        x = libc::fork();
    }

    if x == 0 {
        unsafe {
            libc::sleep(10);
            libc::exit(2);
        }
    }
    /*
    unsafe {
        sleep(5);
    }
     */
    //pub unsafe extern fn fork() -> pid_t
    println!("Exit!");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

const NO_EOF_NEWLINE_COLOR: &str = "\x1b[2m"; // Faint
const NO_EOF_NEWLINE_MARKER: &str = "\\ No newline at end of file";

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Add,
    Remove,
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, remove_line, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in adds.lines() {
        lines.push(format!("{}+{}{}", ADD, add_line, NORMAL))
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds = String::new();
    let mut removes = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                println(output, &line);
            }
            adds.clear();
            removes.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push_str(&line[1..]);
            adds.push('\n');
            last_line_kind = LastLineKind::Add;
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push_str(&line[1..]);
            removes.push('\n');
            last_line_kind = LastLineKind::Remove;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Add => {
                    assert!(adds.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Remove => {
                    assert!(removes.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            println(output, &line);
        }
        adds.clear();
        removes.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in format_adds_and_removes(&adds, &removes) {
        println(output, &line);
    }
}

fn main() {
    if stdin_isatty() {
        eprintln!("Error: Expected input from a pipe");
        exit(1);
    }

    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
        return;
    }

    // FIXME: Prepare env vars LESS and LV for the pager process, just like git does

    // FIXME: Create a pager command
    let status = Command::new("moar").stdin(Stdio::piped()).spawn();

    match status {
        Ok(mut child) => {
            let pager_stdin = child.stdin.as_mut().unwrap();
            highlight_diff(&mut io::stdin().lock(), pager_stdin);
            child.wait().expect("Waiting for pager failed");
        }
        Err(failure) => {
            // FIXME: Try other pager here

            // FIXME: Print pager name as well
            eprintln!("FIXME: Failed to start pager: {}", failure);
            exit(1);
        }
    }
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&"[quotes]\n".to_string(), &"<quotes>\n".to_string()),
            [
                format!(
                    "{}-{}<{}quotes{}>{}{}",
                    REMOVE,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NOT_INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            add("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<commit_msg>Move paging into its own function<commit_after>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

const NO_EOF_NEWLINE_COLOR: &str = "\x1b[2m"; // Faint
const NO_EOF_NEWLINE_MARKER: &str = "\\ No newline at end of file";

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Add,
    Remove,
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, remove_line, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in adds.lines() {
        lines.push(format!("{}+{}{}", ADD, add_line, NORMAL))
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds = String::new();
    let mut removes = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                println(output, &line);
            }
            adds.clear();
            removes.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push_str(&line[1..]);
            adds.push('\n');
            last_line_kind = LastLineKind::Add;
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push_str(&line[1..]);
            removes.push('\n');
            last_line_kind = LastLineKind::Remove;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Add => {
                    assert!(adds.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Remove => {
                    assert!(removes.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            println(output, &line);
        }
        adds.clear();
        removes.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in format_adds_and_removes(&adds, &removes) {
        println(output, &line);
    }
}

/// Try paging using the named pager (`$PATH` will be searched).
///
/// Returns `true` if the pager was found, `false` otherwise.
fn try_pager(pager_name: &str) -> bool {
    // FIXME: Prepare env vars LESS and LV for the pager process, just like git does

    match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
        Ok(mut pager) => {
            let pager_stdin = pager.stdin.as_mut().unwrap();
            highlight_diff(&mut io::stdin().lock(), pager_stdin);

            // FIXME: Report pager exit status if non-zero, together with
            // contents of pager stderr as well if possible.
            pager.wait().expect("Waiting for pager failed");

            return true;
        }
        Err(_) => {
            return false;
        }
    }
}

fn main() {
    if stdin_isatty() {
        eprintln!("Error: Expected input from a pipe");
        exit(1);
    }

    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
        return;
    }

    try_pager("moar");

    // FIXME: Print warning at the end if $PAGER was set to something that
    // doesn't exist.
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&"[quotes]\n".to_string(), &"<quotes>\n".to_string()),
            [
                format!(
                    "{}-{}<{}quotes{}>{}{}",
                    REMOVE,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NOT_INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            add("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![feature(phase, if_let)]

extern crate arena;
extern crate collections;

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

use std::os;
use std::str;
use std::io::fs::File;
use std::task;
use std::default::Default;

mod session;
mod resolve;
mod eval;
mod ast;
mod exec;
mod dumpfile;
mod interner;
mod data_usage;
#[cfg(test)] mod test;

peg_file! grammar("signalspec.rustpeg")

fn main() {
    let sess = session::Session::new();

    let args = os::args();
    let source_utf8 = File::open(&Path::new(args[1].as_slice())).read_to_end().unwrap();
    let source = str::from_utf8(source_utf8.as_slice());
    let module = grammar::module(source.unwrap()).unwrap();

    let mut signal_info = resolve::SignalInfo::new();
    let mut ctx = resolve::Context::new(&sess);

    let modscope = resolve::resolve_module(&sess, &module);

    let main = match modscope.get("main").unwrap() {
        resolve::scope::DefItem(s) => s,
        _ => fail!("Main is not an event"),
    };

    let event = main.resolve_call(&mut ctx, &mut signal_info, Default::default(), None);
    exec::print_step_tree(&event, 0);

    let (s1, mut s2) = exec::Connection::new();
    let (t1, _) = exec::Connection::new();
    task::spawn(proc(){
        let event = event;
        let mut s1 = s1;
        let mut t1 = t1;
    let mut state = eval::State::new();
        let r = exec::exec(&mut state, &event, &mut s1, &mut t1);
        if r {
            println!("Matched");
        } else {
            println!("Failed");
        }
    });

    //dumpfile::write_values(&mut File::create(&Path::new("out.sd")).unwrap(), &s2);
    dumpfile::read_values(&mut File::open(&Path::new("out.sd")).unwrap(), &mut s2);
}
<commit_msg>Command line interface improvements<commit_after>#![allow(dead_code)]
#![feature(phase, if_let, slicing_syntax)]

extern crate arena;
extern crate collections;

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

use std::os;
use std::str;
use std::io;
use std::io::fs::File;
use std::task;
use std::default::Default;

mod session;
mod resolve;
mod eval;
mod ast;
mod exec;
mod dumpfile;
mod interner;
mod data_usage;
#[cfg(test)] mod test;

peg_file! grammar("signalspec.rustpeg")

fn main() {
    let sess = session::Session::new();

    let args = os::args();
    let source_utf8 = File::open(&Path::new(args[1][])).read_to_end().unwrap();
    let source = str::from_utf8(source_utf8.as_slice());
    let module = grammar::module(source.unwrap()).unwrap();

    let mut signal_info = resolve::SignalInfo::new();
    let mut ctx = resolve::Context::new(&sess);

    let modscope = resolve::resolve_module(&sess, &module);

    let main = match modscope.get("main").unwrap() {
        resolve::scope::DefItem(s) => s,
        _ => fail!("Main is not an event"),
    };

    let mut event = main.resolve_call(&mut ctx, &mut signal_info, Default::default(), None);
    data_usage::pass(&mut event, &mut signal_info);
    //exec::print_step_tree(&event, 0);

    let (s1, mut s2) = exec::Connection::new();
    let (mut t1, t2) = exec::Connection::new();

    task::spawn(proc() {
        let mut s1 = s1;
        let mut i = io::stdin();
        dumpfile::read_values(&mut i, &mut s1);
    });

    task::spawn(proc() {
        let mut t2 = t2;
        let mut o = io::stdout();
        dumpfile::write_values(&mut o, &mut t2);
    });

    let mut state = eval::State::new();
    let r = exec::exec(&mut state, &event, &mut s2, &mut t1);

    os::set_exit_status(if r { 0 } else { 1 });
}
<|endoftext|>


<commit_before>#![deny(
    unused_allocation,
    unused_attributes,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_parens,
    unused_results,
    unused_unsafe,
    unused_variables,

    dead_code,
)]

extern crate term;
extern crate git2;
extern crate prompt_buffer;

use prompt_buffer::thread::PromptThread;
use prompt_buffer::buffer::PromptBuffer;

use std::collections::HashMap;
use std::io::{
    self,
    Acceptor,
    Command,
    File,
    fs,
    IoError,
    Listener,
    process,
    stdio,
    timer,
    IoErrorKind
};
use std::io::fs::PathExtensions;
use std::io::net::pipe::{
    UnixListener,
    UnixStream,
};
use std::os;
use std::time::Duration;

mod git;
mod due_date;

fn get_prompt() -> PromptBuffer {
    let mut buf = PromptBuffer::new();
    buf.add_plugin(due_date::DueDatePlugin::new());
    buf.add_plugin(git::GitPlugin::new());

    buf
}

fn exe_changed() -> u64 {
    match os::self_exe_name() {
        Some(exe_path) => {
            match exe_path.stat() {
                Ok(s) => s.modified,
                _ => 0u64
            }
        },
        None => 0u64
    }
}

fn current_pid(pid_path: &Path) -> Result<i32, IoError> {
    let mut file = try!(File::open(pid_path));
    let contents = try!(file.read_to_string());

    match contents.parse() {
        Some(value) => Ok(value),
        None => Err(io::standard_error(IoErrorKind::InvalidInput))
    }
}

macro_rules! sock_try {
    ($x:expr) => {
        match $x {
            Ok(v) => v,
            Err(_) => continue
        }
    };
}

fn main() {
    let stdout_path = Path::new("/var/log/megaprompt/current.out");
    let stderr_path = Path::new("/var/log/megaprompt/current.err");
    let pid_path = Path::new("/var/log/megaprompt/current.pid");
    let socket_path = Path::new("/tmp/megaprompt-socket");

    let args = os::args();
    if args.len() > 1 { // Daemon process
        let _ = stdio::set_stdout(Box::new(File::create(&stdout_path)));
        let _ = stdio::set_stderr(Box::new(File::create(&stderr_path)));

        let last_modified = exe_changed();
        let mut threads: HashMap<Path, PromptThread> = HashMap::new();

        if socket_path.exists() {
            fs::unlink(&socket_path).unwrap();
        }

        let stream = match UnixListener::bind(&socket_path) {
            Err(_) => panic!("Failed to bind to socket"),
            Ok(stream) => stream
        };

        for mut connection in stream.listen().incoming() {
            let c = &mut connection;

            let output = Path::new(sock_try!(c.read_to_string()));
            println!("Preparing to respond to for {}", output.display());

            let keys: Vec<Path> = threads.keys().map(|x| { x.clone() }).collect();
            for path in keys.iter() {
                if !threads.get_mut(path).unwrap().is_alive() {
                    println!("- Remove thread {}", path.display());
                    let _ = threads.remove(path);
                }
            }

            if !threads.contains_key(&output) {
                println!("+ Add thread {}", output.display());
                let _ = threads.insert(output.clone(), PromptThread::new(output.clone(), &get_prompt));
            }

            for path in threads.keys() {
                println!("* Active thread {}", path.display());
            }

            let mut thr = threads.get_mut(&output).unwrap();

            sock_try!(write!(c, "{}", thr.get(&get_prompt)));

            println!("");

            if last_modified != exe_changed() {
                sock_try!(write!(c, "♻  "));
                return;
            }
        }
    } else {
        let current_pid = current_pid(&pid_path).ok().unwrap_or(-1);

        match process::Process::kill(current_pid, 0) {
            Err(_) => {
                // We need to start up the daemon again
                let child = Command::new(args.get(0).as_slice()[0])
                    .arg("daemon")
                    .detached().spawn().unwrap();

                let mut f = match File::create(&pid_path) {
                    Ok(f) => f,
                    Err(_) => panic!("Unable to open pid file")
                };

                write!(&mut f, "{}", child.id()).unwrap();

                println!("Spawned child {}", child.id());

                child.forget();

                timer::sleep(Duration::milliseconds(10));
            }
            _ => {}
        }

        let mut stream = match UnixStream::connect(&socket_path) {
            Err(_) => {
                println!("Can't connect");
                get_prompt().print();
                return;
            },
            Ok(stream) => stream
        };

        write!(&mut stream, "{}", os::make_absolute(&Path::new(".")).unwrap().display()).unwrap();
        stream.close_write().unwrap();
        stream.set_read_timeout(Some(200));
        match stream.read_to_string() {
            Ok(s) => println!("{}", s),
            Err(_) => {
                println!("Response too slow");
                get_prompt().print_fast();
                return;
            }
        }
    }
}

#[test]
fn test_main_does_not_error() {
    main();
}
<commit_msg>Major refactor of main<commit_after>#![deny(
    unused_allocation,
    unused_attributes,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_parens,
    unused_results,
    unused_unsafe,
    unused_variables,

    dead_code,
)]

extern crate term;
extern crate git2;
extern crate prompt_buffer;

use prompt_buffer::thread::PromptThread;
use prompt_buffer::buffer::PromptBuffer;

use std::collections::HashMap;
use std::io::{
    self,
    Acceptor,
    Command,
    File,
    fs,
    IoError,
    Listener,
    process,
    stdio,
    timer,
    IoErrorKind
};
use std::io::fs::PathExtensions;
use std::io::net::pipe::{
    UnixListener,
    UnixStream,
};
use std::os;
use std::time::Duration;

mod git;
mod due_date;

fn get_prompt() -> PromptBuffer {
    let mut buf = PromptBuffer::new();
    buf.add_plugin(due_date::DueDatePlugin::new());
    buf.add_plugin(git::GitPlugin::new());

    buf
}

fn exe_changed() -> u64 {
    match os::self_exe_name() {
        Some(exe_path) => {
            match exe_path.stat() {
                Ok(s) => s.modified,
                _ => 0u64
            }
        },
        None => 0u64
    }
}

fn current_pid(pid_path: &Path) -> Result<i32, IoError> {
    let mut file = try!(File::open(pid_path));
    let contents = try!(file.read_to_string());

    match contents.parse() {
        Some(value) => Ok(value),
        None => Err(io::standard_error(IoErrorKind::InvalidInput))
    }
}

macro_rules! sock_try {
    ($x:expr) => {
        match $x {
            Ok(v) => v,
            Err(_) => continue
        }
    };
}

#[allow(dead_code)]
enum RunMode {
    Daemon,
    Main,
    Test
}

#[allow(dead_code)]
fn main() {
    let args = os::args();
    run(match args.len() {
        2 => RunMode::Daemon,
        1 => RunMode::Main,
        _ => panic!("Number of arguments must be 0 or 1")
    });
}

fn do_daemon(socket_path: &Path) {
    let stdout_path = Path::new("/var/log/megaprompt/current.out");
    let stderr_path = Path::new("/var/log/megaprompt/current.err");

    let _ = stdio::set_stdout(Box::new(File::create(&stdout_path)));
    let _ = stdio::set_stderr(Box::new(File::create(&stderr_path)));

    let last_modified = exe_changed();
    let mut threads: HashMap<Path, PromptThread> = HashMap::new();

    if socket_path.exists() {
        fs::unlink(socket_path).unwrap();
    }

    let stream = match UnixListener::bind(socket_path) {
        Err(_) => panic!("Failed to bind to socket"),
        Ok(stream) => stream
    };

    for mut connection in stream.listen().incoming() {
        let c = &mut connection;

        let output = Path::new(sock_try!(c.read_to_string()));
        println!("Preparing to respond to for {}", output.display());

        let keys: Vec<Path> = threads.keys().map(|x| { x.clone() }).collect();
        for path in keys.iter() {
            if !threads.get_mut(path).unwrap().is_alive() {
                println!("- Remove thread {}", path.display());
                let _ = threads.remove(path);
            }
        }

        if !threads.contains_key(&output) {
            println!("+ Add thread {}", output.display());
            let _ = threads.insert(output.clone(), PromptThread::new(output.clone(), &get_prompt));
        }

        for path in threads.keys() {
            println!("* Active thread {}", path.display());
        }

        let mut thr = threads.get_mut(&output).unwrap();

        sock_try!(write!(c, "{}", thr.get(&get_prompt)));

        println!("");

        if last_modified != exe_changed() {
            sock_try!(write!(c, "♻  "));
            return;
        }
    }
}

fn do_main(socket_path: &Path) {
    let pid_path = Path::new("/var/log/megaprompt/current.pid");

    let current_pid = current_pid(&pid_path).ok().unwrap_or(-1);

    match process::Process::kill(current_pid, 0) {
        Err(_) => {
            // We need to start up the daemon again
            let child = Command::new(os::args().get(0).as_slice()[0])
                .arg("daemon")
                .detached().spawn().unwrap();

            let mut f = match File::create(&pid_path) {
                Ok(f) => f,
                Err(_) => panic!("Unable to open pid file")
            };

            write!(&mut f, "{}", child.id()).unwrap();

            println!("Spawned child {}", child.id());

            child.forget();

            timer::sleep(Duration::milliseconds(10));
        }
        _ => {}
    }

    let mut stream = match UnixStream::connect(socket_path) {
        Err(_) => {
            println!("Can't connect");
            get_prompt().print();
            return;
        },
        Ok(stream) => stream
    };

    write!(&mut stream, "{}", os::make_absolute(&Path::new(".")).unwrap().display()).unwrap();
    stream.close_write().unwrap();
    stream.set_read_timeout(Some(200));
    match stream.read_to_string() {
        Ok(s) => println!("{}", s),
        Err(_) => {
            println!("Response too slow");
            get_prompt().print_fast();
            return;
        }
    }
}

fn run(mode: RunMode) {
    let socket_path = Path::new("/tmp/megaprompt-socket");

    match mode {
        RunMode::Daemon => do_daemon(&socket_path),
        RunMode::Main => do_main(&socket_path),
        RunMode::Test => {}
    }
}

#[test]
fn test_main_does_not_error() {
    run(RunMode::Test);
}
<|endoftext|>


<commit_before>extern crate piston_window;
extern crate itertools_num;
extern crate opengl_graphics;
extern crate rand;

mod drawing;
mod game;
mod models;
mod traits;

use piston_window::{Button, Event, EventLoop, Input, Motion, OpenGL, PistonWindow,
    WindowSettings};
use opengl_graphics::GlGraphics;

use game::Game;

fn main() {
    let opengl = OpenGL::V3_2;

    let game_size: drawing::Size = drawing::Size::new(1024.0, 600.0);
    let mut game = Game::new(game_size);

    let mut window: PistonWindow = WindowSettings::new(
        "Rocket!", [game_size.width as u32, game_size.height as u32])
        .opengl(opengl).samples(8).exit_on_esc(true).build().unwrap();

    window.set_ups(60);
    window.set_max_fps(60);

    let mut gl = GlGraphics::new(opengl);

    while let Some(e) = window.next() {
        // Event handling
        match e {
            Event::Input(Input::Press(Button::Keyboard(key))) => {
                game.key_press(key);
            }

            Event::Input(Input::Release(Button::Keyboard(key))) => {
                game.key_release(key);
            }

            Event::Input(Input::Press(Button::Controller(button))) => {
                game.button_press(button);
            }

            Event::Input(Input::Release(Button::Controller(button))) => {
                game.button_release(button);
            }

            // Controller Axis are Move Input types
            Event::Input(Input::Move(Motion::ControllerAxis(axis))) => {
                game.handle_axis(axis);
            }

            Event::Render(args) => {
                gl.draw(args.viewport(), |c, g| game.render(c, g));
            }

            Event::Update(args) => {
                game.update(args.dt);
            }

            _ => {}
        }
    }
}
<commit_msg>Deny missing docs.<commit_after>#![deny(missing_docs)]

//! A 2D toy game written in Rust, using the Piston library.

extern crate piston_window;
extern crate itertools_num;
extern crate opengl_graphics;
extern crate rand;

mod drawing;
mod game;
mod models;
mod traits;

use piston_window::{Button, Event, EventLoop, Input, Motion, OpenGL, PistonWindow,
    WindowSettings};
use opengl_graphics::GlGraphics;

use game::Game;

fn main() {
    let opengl = OpenGL::V3_2;

    let game_size: drawing::Size = drawing::Size::new(1024.0, 600.0);
    let mut game = Game::new(game_size);

    let mut window: PistonWindow = WindowSettings::new(
        "Rocket!", [game_size.width as u32, game_size.height as u32])
        .opengl(opengl).samples(8).exit_on_esc(true).build().unwrap();

    window.set_ups(60);
    window.set_max_fps(60);

    let mut gl = GlGraphics::new(opengl);

    while let Some(e) = window.next() {
        // Event handling
        match e {
            Event::Input(Input::Press(Button::Keyboard(key))) => {
                game.key_press(key);
            }

            Event::Input(Input::Release(Button::Keyboard(key))) => {
                game.key_release(key);
            }

            Event::Input(Input::Press(Button::Controller(button))) => {
                game.button_press(button);
            }

            Event::Input(Input::Release(Button::Controller(button))) => {
                game.button_release(button);
            }

            // Controller Axis are Move Input types
            Event::Input(Input::Move(Motion::ControllerAxis(axis))) => {
                game.handle_axis(axis);
            }

            Event::Render(args) => {
                gl.draw(args.viewport(), |c, g| game.render(c, g));
            }

            Event::Update(args) => {
                game.update(args.dt);
            }

            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>extern crate termion;
#[macro_use] extern crate macro_attr;
#[macro_use] extern crate enum_derive;

use termion::event::{Event, Key};
use termion::raw::{RawTerminal, IntoRawMode};
use termion::screen::AlternateScreen;
use termion::input::TermRead;
use std::io::{Write, stdin, stdout};
use std::cmp;

mod keybinds;
use keybinds::*;

mod file;
use file::*;

type Terminal = AlternateScreen<RawTerminal<std::io::Stdout>>;

fn render_file(mut stdout: &mut Terminal, file: &File) {
    use termion::{cursor, color};
    let (screen_width, screen_height) = termion::terminal_size().unwrap();

    let mut x = 1;
    let mut y = 1;

    for (line_number, line) in file.lines.iter().enumerate() {
        write!(stdout, "{}{}{:3}{} ",
               color::Fg(color::LightBlack),
               cursor::Goto(x, y),
               (line_number + 1),
               color::Fg(color::Reset)).unwrap();
        x += 4;
        let mut line_start = 0;
        let mut line_end = cmp::min(line.len(), (screen_width - x) as usize);
        write!(stdout, "{}{}", cursor::Goto(x, y), &line[line_start..line_end]).unwrap();
        while line_end < line.len() {
            x = 5;
            y += 1;
            line_start = line_end;
            line_end += cmp::min(line.len() - line_end, (screen_width - x) as usize);
            write!(stdout, "{}{}", cursor::Goto(x, y), &line[line_start..line_end]).unwrap();
        }
        y += 1;
        x = 1;
    }
}

fn render_footer(mut stdout: &mut Terminal, keys: &KeybindTable) {
    use termion::{color, cursor};
    let (screen_width, screen_height) = termion::terminal_size().unwrap();

    let mut x = 1;
    let mut y = screen_height - 1;
    for (key, action) in keys.entries() {
        write!(stdout, "{}{}{}{}",
               cursor::Goto(x, y),
               color::Bg(color::White),
               color::Fg(color::Black),
               key).unwrap();
        write!(stdout, "{}{} {}",
               color::Bg(color::Reset),
               color::Fg(color::Reset),
               action).unwrap();
        x += 16;
        if x > screen_width {
            x = 1;
            y -= 1;
        }
    }
}

fn main() {
    let keys = KeybindTable::default();
    let stdin = stdin();
    let mut stdout = AlternateScreen::from(stdout().into_raw_mode().unwrap());
    write!(stdout, "{}", termion::clear::All).unwrap();
    render_footer(&mut stdout, &keys);
    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
    let file = File::open("README.md");
    render_file(&mut stdout, &file);
    stdout.flush().unwrap();
    for c in stdin.events() {
        let evt = c.unwrap();
        match evt {
            Event::Key(Key::Ctrl(k)) => {
                match keys.lookup(Key::Ctrl(k)) {
                    Some(Command::Quit) => break,
                    _ => ()
                }
            },
            _ => {}
        }
        stdout.flush().unwrap();
    }
}
<commit_msg>Do a bit of navigation<commit_after>extern crate termion;
#[macro_use] extern crate macro_attr;
#[macro_use] extern crate enum_derive;

use termion::event::{Event, Key};
use termion::raw::{RawTerminal, IntoRawMode};
use termion::screen::AlternateScreen;
use termion::input::TermRead;
use termion::cursor;
use termion::color;
use std::io::{Write, stdin, stdout};
use std::cmp;

mod keybinds;
use keybinds::*;

mod file;
use file::*;

type Terminal = AlternateScreen<RawTerminal<std::io::Stdout>>;

const LINENO_CHARS : u16 = 3;

fn render_file(mut stdout: &mut Terminal, file: &File) {
    let (screen_width, screen_height) = termion::terminal_size().unwrap();

    let mut x = 1;
    let mut y = 1;

    for (line_number, line) in file.lines.iter().enumerate() {
        write!(stdout, "{}{}{:3}{} ",
               color::Fg(color::LightBlack),
               cursor::Goto(x, y),
               (line_number + 1),
               color::Fg(color::Reset)).unwrap();
        x += LINENO_CHARS + 1;
        let mut line_start = 0;
        let mut line_end = cmp::min(line.len(), (screen_width - x) as usize);
        write!(stdout, "{}{}", cursor::Goto(x, y), &line[line_start..line_end]).unwrap();
        while line_end < line.len() {
            x = LINENO_CHARS + 2;
            y += 1;
            line_start = line_end;
            line_end += cmp::min(line.len() - line_end, (screen_width - x) as usize);
            write!(stdout, "{}{}", cursor::Goto(x, y), &line[line_start..line_end]).unwrap();
        }
        y += 1;
        x = 1;
    }
}

fn render_footer(mut stdout: &mut Terminal, keys: &KeybindTable) {
    let (screen_width, screen_height) = termion::terminal_size().unwrap();

    let mut x = 1;
    let mut y = screen_height - 1;
    for (key, action) in keys.entries() {
        write!(stdout, "{}{}{}{}",
               cursor::Goto(x, y),
               color::Bg(color::White),
               color::Fg(color::Black),
               key).unwrap();
        write!(stdout, "{}{} {}",
               color::Bg(color::Reset),
               color::Fg(color::Reset),
               action).unwrap();
        x += 16;
        if x > screen_width {
            x = 1;
            y -= 1;
        }
    }
}

fn main() {
    let keys = KeybindTable::default();
    let stdin = stdin();
    let mut stdout = AlternateScreen::from(stdout().into_raw_mode().unwrap());
    write!(stdout, "{}", termion::clear::All).unwrap();
    render_footer(&mut stdout, &keys);
    let file = File::open("README.md");
    render_file(&mut stdout, &file);
    let (mut cursor_x, mut cursor_y) = (1, 1);
    write!(stdout, "{}", cursor::Goto(cursor_x + LINENO_CHARS + 1, cursor_y)).unwrap();
    stdout.flush().unwrap();
    for c in stdin.events() {
        let evt = c.unwrap();
        match evt {
            Event::Key(Key::Ctrl(k)) => {
                match keys.lookup(Key::Ctrl(k)) {
                    Some(Command::Quit) => break,
                    _ => ()
                }
            },
            Event::Key(Key::Left) => cursor_x -= 1,
            Event::Key(Key::Right) => cursor_x += 1,
            Event::Key(Key::Up) => cursor_y -= 1,
            Event::Key(Key::Down) => cursor_y += 1,
            _ => {}
        }
        let (screen_width, screen_height) = termion::terminal_size().unwrap();
        if cursor_x < 1 { cursor_x = 1; }
        if cursor_x > screen_width - LINENO_CHARS - 2 { cursor_x = screen_width - LINENO_CHARS - 2; }
        if cursor_y < 1 { cursor_y = 1; }
        if cursor_y > screen_height - 3 { cursor_y = screen_height - 3; }
        write!(stdout, "{}", cursor::Goto(cursor_x + LINENO_CHARS + 1, cursor_y)).unwrap();
        stdout.flush().unwrap();
    }
}
<|endoftext|>