<commit_before>extern crate mio;
extern crate bytes;
extern crate toml;
extern crate openssl;
extern crate hyper;

#[macro_use]
extern crate log;

extern crate env_logger;

use std::io;
use std::net::SocketAddr;
use mio::*;
use mio::tcp::TcpListener;
use mio::util::Slab;

const INVALID: Token = Token(0);
const ACCEPTOR: Token = Token(1);
const FLOW: Token = Token(2);

const OUTMASK: usize = 2147483648; //2 ** 31
const NOTMASK: usize = !OUTMASK;

const EMPTY_BUF: [u8; 0] = [];

mod api;
mod config;
mod tls;
mod multiplex;
mod flow;

use multiplex::{Multiplexer, MR};

use flow::Flow;

struct Nexus {
    // main server socket (that accepts inbound connections)
    acceptor: TcpListener,

    // token of the acceptor
    token: Token,

    // a list of all inbound and outbound connections
    conns: Slab<Flow>,

    // size for the buffer between the inbound/outbound streams
    bufsize: usize,

    multiplexer: Box<Multiplexer>,
}

impl Nexus {
    fn new(acceptor: TcpListener, bufsize: usize, multiplexer: Box<Multiplexer>) -> Nexus {
        Nexus {
            acceptor: acceptor,

            // I don't use Token(0) because kqueue will send stuff to Token(0)
            // by default causing really strange behavior. This way, if I see
            // something as Token(0), I know there are kqueue shenanigans
            // going on.
            token: ACCEPTOR,

            // SERVER is Token(1), so start after that
            // we can deal with a max of 126 connections
            conns: Slab::new_starting_at(FLOW, 128),

            bufsize: bufsize,

            multiplexer: multiplexer,
        }
    }

    /// Register Server with the event loop.
    ///
    /// This keeps the registration details neatly tucked away inside of our implementation.
    fn register(&mut self, event_loop: &mut EventLoop<Nexus>) -> io::Result<()> {
        event_loop.register(
            &self.acceptor,
            self.token,
            EventSet::readable(),
            PollOpt::edge(), // | PollOpt::oneshot()
        ).or_else(|e| {
            error!("Failed to register server {:?}, {:?}", self.token, e);
            Err(e)
        })
    }

    /// Accept a _new_ client connection.
    ///
    /// The server will keep track of the new connection and forward any events from the event loop
    /// to this connection.
    fn accept(&mut self, event_loop: &mut EventLoop<Nexus>) {
        debug!("tnexus accepting new socket(s)");

        loop {
        // Log an error if there is no socket, but otherwise move on so we do not tear down the
        // entire server.
        let inbound = match self.acceptor.accept() {
            Ok(s) => {
                match s {
                    Some(sock) => sock,
                    None => {
                        debug!("No more new sockets");
                        return;
                    }
                }
            },
            Err(e) => {
                error!("Failed to accept new socket, {:?}", e);
                return;
            }
        };

        let inbound_stream = inbound.0;

        let bufsize = self.bufsize;

        match self.conns.insert_with(|token| {
            debug!("Inserting {:?} into slab", token);
            Flow::new(inbound_stream, token, bufsize)
        }) {
            Some(token) => {
                match self.find_connection_by_token(token).inb.register(event_loop) {
                    Ok(_) => {
                        debug!("Registered inbound token {:?}", token);

                        let mr = {
                            self.multiplexer.destination(&EMPTY_BUF)
                        };
                        match mr {
                            MR::Match(outbound) => {
                                //setup outbound immediately!
                                self.find_connection_by_token(token).set_outbound(outbound, bufsize, event_loop);
                            },
                            _ => (),
                        }
                    },
                    Err(e) => {
                        error!("Failed to register inbound {:?} connection with event loop, {:?}", token, e);
                        self.conns.remove(token);
                    }
                }
            },
            None => {
                // If we fail to insert, the socks will go out of scope and be dropped.
                error!("Failed to insert into slab");
            }
        };

        }
    }

    fn stop_flow(&mut self, token: Token) {
        debug!("Stopping flow {:?}", token);
        self.conns.remove(token);
    }

    /// Find a connection in the slab using the given token.
    fn find_connection_by_token<'a>(&'a mut self, token: Token) -> &'a mut Flow {
        &mut self.conns[token]
    }

    /*
    fn with_flow<F, R>(&mut self, token: Token, mut closure: F) -> Option<R>
        where F: FnMut(&mut Flow) -> R {

        match self.conns.get_mut(token) {
            None => {
                warn!("Token not found: {:?}", token);
                None
            },
            Some(flow) => {
                Some(closure(flow))
            }
        }
    }
    */
}

impl Handler for Nexus {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Nexus>, _token: Token, events: EventSet) {
        assert!(_token != INVALID, "[BUG]: Received event for Token(0)");

        let tokval = _token.as_usize();
        let inb = tokval & OUTMASK == 0;
        let token = Token(tokval & NOTMASK);

        debug!("READY {:X} {} {:?} {:?}", tokval, inb, token, events);

        if events.is_error() {
            warn!("Final event {:?} for token {:X}", events, tokval);
        }

        // We never expect a write event for our `Server` token . A write event for any other token
        // should be handed off to that connection.
        if events.is_writable() {
            debug!("Write event for {:X}", tokval);
            assert!(self.token != token, "Received writable event for Server");

            let write_result = match self.conns.get_mut(token) {
                None => {
                    warn!("Unknown token {:X}", tokval);
                    return;
                },
                Some(flow) => {
                    flow.write(inb, event_loop)
                }
            };

            if !write_result {
                self.stop_flow(token);
                return;
            }
        }

        // A read event for our `Server` token means we are establishing a new connection. A read
        // event for any other token should be handed off to that connection.
        if events.is_readable() {
            if ACCEPTOR == token {
                self.accept(event_loop);
            } else {
                let should_stop = match self.conns.get_mut(token) {
                    None => {
                        warn!("Ignoring non-existing readable token: {:?}", token);
                        false
                    },
                    Some(flow) => {
                        if flow.read(inb, self.bufsize, &self.multiplexer, event_loop) {
                            false
                        } else {
                            warn!("Read returned false for token: {:?}", token);
                            true
                        }
                    }
                };

                if should_stop {
                    self.stop_flow(token);
                }
            }
        }
    }
}

fn main() {
    env_logger::init().ok().expect("Failed to init logger");

    info!("Starting tnexus...");

    let args = config::get_args();

    debug!("args: {:?}", args);

    let endpoint_addr: SocketAddr = args.listen.parse()
        .ok().expect("Failed to parse server enpoint");

    // Setup the acceptor socket
    let acceptor = TcpListener::bind(&endpoint_addr)
        .ok().expect("Failed to bind server endpoint");

    // Create an event loop
    let mut event_loop = EventLoop::new()
        .ok().expect("Could not initialize MIO event loop");

    let mut nexus = Nexus::new(
        acceptor,
        args.bufsize,
        args.destination);

    // Start listening for incoming connections
    nexus.register(&mut event_loop)
        .ok().expect("Failed to register acceptor in event loop");

    // Start handling events
    event_loop.run(&mut nexus)
        .ok().expect("Failed to start event loop");

    info!("Over.");
}
<commit_msg>Fixed compilation warnings.<commit_after>extern crate mio;
extern crate bytes;
extern crate toml;
extern crate openssl;
extern crate hyper;

#[macro_use]
extern crate log;

extern crate env_logger;

use std::io;
use std::net::SocketAddr;
use mio::*;
use mio::tcp::TcpListener;
use mio::util::Slab;

const INVALID: Token = Token(0);
const ACCEPTOR: Token = Token(1);
const FLOW: Token = Token(2);

const OUTMASK: usize = 2147483648; //2 ** 31
const NOTMASK: usize = !OUTMASK;

const EMPTY_BUF: [u8; 0] = [];

mod api;
mod config;
mod tls;
mod multiplex;
mod flow;

use multiplex::{Multiplexer, MR};

use flow::Flow;

pub struct Nexus {
    // main server socket (that accepts inbound connections)
    acceptor: TcpListener,

    // token of the acceptor
    token: Token,

    // a list of all inbound and outbound connections
    conns: Slab<Flow>,

    // size for the buffer between the inbound/outbound streams
    bufsize: usize,

    multiplexer: Box<Multiplexer>,
}

impl Nexus {
    fn new(acceptor: TcpListener, bufsize: usize, multiplexer: Box<Multiplexer>) -> Nexus {
        Nexus {
            acceptor: acceptor,

            // I don't use Token(0) because kqueue will send stuff to Token(0)
            // by default causing really strange behavior. This way, if I see
            // something as Token(0), I know there are kqueue shenanigans
            // going on.
            token: ACCEPTOR,

            // SERVER is Token(1), so start after that
            // we can deal with a max of 126 connections
            conns: Slab::new_starting_at(FLOW, 128),

            bufsize: bufsize,

            multiplexer: multiplexer,
        }
    }

    /// Register Server with the event loop.
    ///
    /// This keeps the registration details neatly tucked away inside of our implementation.
    fn register(&mut self, event_loop: &mut EventLoop<Nexus>) -> io::Result<()> {
        event_loop.register(
            &self.acceptor,
            self.token,
            EventSet::readable(),
            PollOpt::edge(), // | PollOpt::oneshot()
        ).or_else(|e| {
            error!("Failed to register server {:?}, {:?}", self.token, e);
            Err(e)
        })
    }

    /// Accept a _new_ client connection.
    ///
    /// The server will keep track of the new connection and forward any events from the event loop
    /// to this connection.
    fn accept(&mut self, event_loop: &mut EventLoop<Nexus>) {
        debug!("tnexus accepting new socket(s)");

        loop {
        // Log an error if there is no socket, but otherwise move on so we do not tear down the
        // entire server.
        let inbound = match self.acceptor.accept() {
            Ok(s) => {
                match s {
                    Some(sock) => sock,
                    None => {
                        debug!("No more new sockets");
                        return;
                    }
                }
            },
            Err(e) => {
                error!("Failed to accept new socket, {:?}", e);
                return;
            }
        };

        let inbound_stream = inbound.0;

        let bufsize = self.bufsize;

        match self.conns.insert_with(|token| {
            debug!("Inserting {:?} into slab", token);
            Flow::new(inbound_stream, token, bufsize)
        }) {
            Some(token) => {
                match self.find_connection_by_token(token).inb.register(event_loop) {
                    Ok(_) => {
                        debug!("Registered inbound token {:?}", token);

                        let mr = {
                            self.multiplexer.destination(&EMPTY_BUF)
                        };
                        match mr {
                            MR::Match(outbound) => {
                                //setup outbound immediately!
                                self.find_connection_by_token(token).set_outbound(outbound, bufsize, event_loop);
                            },
                            _ => (),
                        }
                    },
                    Err(e) => {
                        error!("Failed to register inbound {:?} connection with event loop, {:?}", token, e);
                        self.conns.remove(token);
                    }
                }
            },
            None => {
                // If we fail to insert, the socks will go out of scope and be dropped.
                error!("Failed to insert into slab");
            }
        };

        }
    }

    fn stop_flow(&mut self, token: Token) {
        debug!("Stopping flow {:?}", token);
        self.conns.remove(token);
    }

    /// Find a connection in the slab using the given token.
    fn find_connection_by_token<'a>(&'a mut self, token: Token) -> &'a mut Flow {
        &mut self.conns[token]
    }

    /*
    fn with_flow<F, R>(&mut self, token: Token, mut closure: F) -> Option<R>
        where F: FnMut(&mut Flow) -> R {

        match self.conns.get_mut(token) {
            None => {
                warn!("Token not found: {:?}", token);
                None
            },
            Some(flow) => {
                Some(closure(flow))
            }
        }
    }
    */
}

impl Handler for Nexus {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Nexus>, _token: Token, events: EventSet) {
        assert!(_token != INVALID, "[BUG]: Received event for Token(0)");

        let tokval = _token.as_usize();
        let inb = tokval & OUTMASK == 0;
        let token = Token(tokval & NOTMASK);

        debug!("READY {:X} {} {:?} {:?}", tokval, inb, token, events);

        if events.is_error() {
            warn!("Final event {:?} for token {:X}", events, tokval);
        }

        // We never expect a write event for our `Server` token . A write event for any other token
        // should be handed off to that connection.
        if events.is_writable() {
            debug!("Write event for {:X}", tokval);
            assert!(self.token != token, "Received writable event for Server");

            let write_result = match self.conns.get_mut(token) {
                None => {
                    warn!("Unknown token {:X}", tokval);
                    return;
                },
                Some(flow) => {
                    flow.write(inb, event_loop)
                }
            };

            if !write_result {
                self.stop_flow(token);
                return;
            }
        }

        // A read event for our `Server` token means we are establishing a new connection. A read
        // event for any other token should be handed off to that connection.
        if events.is_readable() {
            if ACCEPTOR == token {
                self.accept(event_loop);
            } else {
                let should_stop = match self.conns.get_mut(token) {
                    None => {
                        warn!("Ignoring non-existing readable token: {:?}", token);
                        false
                    },
                    Some(flow) => {
                        if flow.read(inb, self.bufsize, &self.multiplexer, event_loop) {
                            false
                        } else {
                            warn!("Read returned false for token: {:?}", token);
                            true
                        }
                    }
                };

                if should_stop {
                    self.stop_flow(token);
                }
            }
        }
    }
}

fn main() {
    env_logger::init().ok().expect("Failed to init logger");

    info!("Starting tnexus...");

    let args = config::get_args();

    debug!("args: {:?}", args);

    let endpoint_addr: SocketAddr = args.listen.parse()
        .ok().expect("Failed to parse server enpoint");

    // Setup the acceptor socket
    let acceptor = TcpListener::bind(&endpoint_addr)
        .ok().expect("Failed to bind server endpoint");

    // Create an event loop
    let mut event_loop = EventLoop::new()
        .ok().expect("Could not initialize MIO event loop");

    let mut nexus = Nexus::new(
        acceptor,
        args.bufsize,
        args.destination);

    // Start listening for incoming connections
    nexus.register(&mut event_loop)
        .ok().expect("Failed to register acceptor in event loop");

    // Start handling events
    event_loop.run(&mut nexus)
        .ok().expect("Failed to start event loop");

    info!("Over.");
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)] extern crate regex;
extern crate git2;
extern crate core;
extern crate rustc_serialize;
extern crate docopt;

use git2::{Repository, Commit, Diff, DiffFormat, Oid, Error};
use docopt::Docopt;
use std::collections::HashMap;
// use std::old_io::BufferedReader;
// use std::old_io::File;
use std::str;
// use regex::Regex;
use rustc_serialize::json;
use std::fmt::Display;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit [--web | <repo> [<old> <new>] | --help]

Examples:
  transit --web             Spawn a web service.
  transit $REPO             Output the results of a revwalk through a repo.
  transit $REPO $ID1 $ID2   Output the data for a pair of commits.
  transit --help            Display this message.

Output is in JSON.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_web: bool,
    arg_repo: Option<String>,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    print!("{:?}", args);

    if args.flag_web {
        unimplemented!();
    } else if let Some(path_string) = args.arg_repo {
        // Validate values.
        let path = Path::new(&path_string);
        let repo = Repository::discover(&path)
            .ok().expect("Unable to find repo.");
        let old = args.arg_old
            .and_then(|string| Oid::from_str(&string[..]).ok());
        let new = args.arg_new
            .and_then(|string| Oid::from_str(&string[..]).ok());
        // Dispatch.
        let output = if let (Some(old_id), Some(new_id)) = (old, new) {
            process_commits(repo, old_id, new_id).unwrap();
        } else {
            process_repo(repo).unwrap();
        };
        println!("{}", json::as_pretty_json(&output).indent(4));
    } else {
        unreachable!();
    }
}

#[derive(Debug, PartialEq, Eq)]
enum FoundState {
    Added, Deleted
}

fn process_commits(repo: Repository, old_id: Oid, new_id: Oid) -> Result<OutputSet, git2::Error> {
    // Compare a specific commit pair.
    let old = repo.find_commit(old_id);
    let new = repo.find_commit(new_id);
    match (old, new) {
        (Ok(old_commit), Ok(new_commit)) => {
            let output = try!(find_moves(&repo, &old_commit, &new_commit));
            Ok(OutputSet {
                old: TransitOid(old_id),
                old_time: old_commit.time().seconds(), // Seconds from Epoch
                new: TransitOid(new_id),
                new_time: new_commit.time().seconds(), // Seconds from Epoch
                outputs: output,
            })
        },
        _ => Err(git2::Error::from_str("Commit IDs were not valid.")),
    }
}

fn process_repo(repo: Repository) -> Result<Vec<OutputSet>, git2::Error> {
    // Pull up the revwalk.
    // Revwalk.
    let mut revwalk = try!(repo.revwalk());
    // Setup some options.
    revwalk.simplify_first_parent(); // TODO: Maybe remove?
    let mut flags = git2::Sort::empty();
    flags.insert(git2::SORT_TIME);
    flags.insert(git2::SORT_TOPOLOGICAL);
    revwalk.set_sorting(flags);
    // Push HEAD to the revwalk.
    try!(revwalk.push_head());
    // We sadly must collect here to use `.windows()`
    let history = revwalk.collect::<Vec<Oid>>();
    let mut output = Vec::with_capacity(history.len());
    // Walk through each pair of commits.
    for pair in history.windows(2) {
        let (old_id, new_id) = (pair[0], pair[1]);
        if let (Ok(old), Ok(new)) = (repo.find_commit(old_id), repo.find_commit(new_id)) {
            let detected = try!(find_moves(&repo, &old, &new));
            output.push(OutputSet {
                old: TransitOid(old_id),
                old_time: old.time().seconds(), // Seconds from Epoch
                new: TransitOid(new_id),
                new_time: new.time().seconds(), // Seconds from Epoch
                outputs: detected,
            });
        } else {
            continue;
        }
    }
    Ok(output)
}

fn format_key(key: String) -> String {
    let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    let trim = regex!(r"^[\s]+|[\s]+$");
    let result = remove_whitespace.replace_all(&key[..], "");
    trim.replace_all(&result[..], "")
}

#[derive(Debug)]
struct Found {
    filename: String,
    key: String,
    state: FoundState,
    start_position: u32,
    line_count: u32,
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

    let mut founds: Vec<Found> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path = String::new();
    let mut new_path = String::new();

    let mut line_count: u32 = 0;
    let mut start_position: u32 = 0;

    // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
    // It's a bit weird, but I think it will provide the necessary information.
    diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // if delta.nfiles() != 2 {
        //     // This is diff with only one side, and thus can't have a move.
        //     return true;
        // }
        // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

        // Thinking:
        //  * If is not a hunk, keep going.
        //  * If it's a hunk, do regex magic.
        //  * Stick regex output into a hashmap as a hash.
        //  * Later, we will iterate through and output pased on the values.
        // Filter out all the boring context lines.
        // If we're not interested in this line just return since it will iterate to the next.
        if maybe_hunk.is_none() { return true }; // Return early.

        //dump_diffline(&line);
        //dump_diffdelta(&delta);
        //dump_diffhunk(&maybe_hunk.unwrap());

        old_path = match delta.old_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };
        new_path = match delta.new_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };

        match line.origin() {
            // Additions
            '+' | '>' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                added.push_str(line_str);

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                        line_count = 0;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Addition => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Addition;
                true
            },
            // Deletions
            '-' | '<' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                deleted.push_str(line_str);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                        line_count = 0;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Deletion => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Deletion;
                true
            },
            // Other
            _         => {
                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
        }
    }).ok(); // We don't care if we exit early. That's fine.

    // Grab last one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path.clone(),
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path.clone(),
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        _ => (),
    }

    return founds;
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
    let old_tree = try!(old.tree());
    let new_tree = try!(new.tree());
    // Build up a diff of the two trees.
    let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let founds: Vec<Found> = find_additions_and_deletions(diff);

    let mut moves: Vec<Output> = Vec::new();
    let mut map: HashMap<String, Found> = HashMap::new();

    for f in founds {
        if f.key.len() == 0 { continue; }

        if map.contains_key(&f.key) {
            let q = map.get(&f.key).unwrap();

            // If both states are added, there are no moves.
            // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
            if f.state == q.state {
                return Ok(Vec::<Output>::new())
            }

            let output: Output;

            match f.state {
                FoundState::Added => {
                    output = Output {
                        old_filename: q.filename.clone(),
                        new_filename: f.filename.clone(),
                        origin_line: q.start_position,
                        destination_line: f.start_position,
                        num_lines: f.line_count,
                    };
                },
                FoundState::Deleted => {
                    output = Output {
                        old_filename: f.filename.clone(),
                        new_filename: q.filename.clone(),
                        origin_line: f.start_position,
                        destination_line: q.start_position,
                        num_lines: f.line_count,
                    };
                },
            }

            moves.push(output);
        } else {
            map.insert(f.key.clone(), f);
        }
    }

    return Ok(moves);
}

#[derive(Debug, RustcEncodable)]
struct OutputSet {
    old: TransitOid,
    old_time: i64, // Seconds from Epoch
    new: TransitOid,
    new_time: i64, // Seconds from Epoch
    outputs: Vec<Output>,
}

#[derive(Debug, RustcEncodable)]
struct Output {
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct TransitOid(Oid);
impl Display for TransitOid {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Display::fmt(&self.0, f)
    }
}

impl rustc_serialize::Encodable for TransitOid {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(&format!("{}", self)[..])
    }
}
<commit_msg>Use modules.<commit_after>#![feature(core)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)] extern crate regex;
extern crate git2;
extern crate core;
extern crate rustc_serialize;
extern crate docopt;

use git2::{Repository, Oid};
use docopt::Docopt;
use rustc_serialize::json;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit [--web | <repo> [<old> <new>] | --help]

Examples:
  transit --web             Spawn a web service.
  transit $REPO             Output the results of a revwalk through a repo.
  transit $REPO $ID1 $ID2   Output the data for a pair of commits.
  transit --help            Display this message.

Output is in JSON.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_web: bool,
    arg_repo: Option<String>,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_web {
        unimplemented!();
    } else if let Some(path_string) = args.arg_repo {
        // Validate values.
        let path = Path::new(&path_string);
        let repo = Repository::discover(&path)
            .ok().expect("Unable to find repo.");
        let old = args.arg_old
            .and_then(|string| Oid::from_str(&string[..]).ok());
        let new = args.arg_new
            .and_then(|string| Oid::from_str(&string[..]).ok());
        // Dispatch.
        if let (Some(old_id), Some(new_id)) = (old, new) {
            let output = processor::process_commits(repo, old_id, new_id).unwrap();
            println!("{}", json::as_pretty_json(&output).indent(4));
        } else {
            let output = processor::process_repo(repo).unwrap();
            println!("{}", json::as_pretty_json(&output).indent(4));
        };

    } else {
        unreachable!();
    }
}

mod processor {
    use {git2, rustc_serialize};
    use git2::{Repository, Commit, Diff, DiffFormat, Oid};
    use std::collections::HashMap;
    use std::fmt;
    use std::str;

    pub fn process_commits(repo: Repository, old_id: Oid, new_id: Oid) -> Result<OutputSet, git2::Error> {
        // Compare a specific commit pair.
        let old = repo.find_commit(old_id);
        let new = repo.find_commit(new_id);
        match (old, new) {
            (Ok(old_commit), Ok(new_commit)) => {
                let output = try!(find_moves(&repo, &old_commit, &new_commit));
                Ok(OutputSet {
                    old: TransitOid(old_id),
                    old_time: old_commit.time().seconds(), // Seconds from Epoch
                    new: TransitOid(new_id),
                    new_time: new_commit.time().seconds(), // Seconds from Epoch
                    outputs: output,
                })
            },
            _ => Err(git2::Error::from_str("Commit IDs were not valid.")),
        }
    }

    pub fn process_repo(repo: Repository) -> Result<Vec<OutputSet>, git2::Error> {
        // Pull up the revwalk.
        // Revwalk.
        let mut revwalk = try!(repo.revwalk());
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        try!(revwalk.push_head());
        // We sadly must collect here to use `.windows()`
        let history = revwalk.collect::<Vec<Oid>>();
        let mut output = Vec::with_capacity(history.len());
        // Walk through each pair of commits.
        for pair in history.windows(2) {
            let (old_id, new_id) = (pair[0], pair[1]);
            if let (Ok(old), Ok(new)) = (repo.find_commit(old_id), repo.find_commit(new_id)) {
                let out = OutputSet {
                    old: TransitOid(old_id),
                    old_time: old.time().seconds(), // Seconds from Epoch
                    new: TransitOid(new_id),
                    new_time: new.time().seconds(), // Seconds from Epoch
                    outputs: try!(find_moves(&repo, &old, &new)),
                };
                output.push(out);
            } else {
                continue;
            }
        }
        Ok(output)
    }

    #[derive(Debug, PartialEq, Eq)]
    enum FoundState {
        Added, Deleted
    }

    fn format_key(key: String) -> String {
        let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
        let trim = regex!(r"^[\s]+|[\s]+$");
        let result = remove_whitespace.replace_all(&key[..], "");
        trim.replace_all(&result[..], "")
    }

    #[derive(Debug)]
    struct Found {
        filename: String,
        key: String,
        state: FoundState,
        start_position: u32,
        line_count: u32,
    }

    #[derive(Debug)]
    enum State {
        Other, Addition, Deletion
    }

    fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

        let mut founds: Vec<Found> = Vec::new();

        let mut state = State::Other;
        let mut added = String::new();
        let mut deleted = String::new();
        let mut old_path = String::new();
        let mut new_path = String::new();

        let mut line_count: u32 = 0;
        let mut start_position: u32 = 0;

        // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
        // It's a bit weird, but I think it will provide the necessary information.
        diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

            // if delta.nfiles() != 2 {
            //     // This is diff with only one side, and thus can't have a move.
            //     return true;
            // }
            // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

            // Thinking:
            //  * If is not a hunk, keep going.
            //  * If it's a hunk, do regex magic.
            //  * Stick regex output into a hashmap as a hash.
            //  * Later, we will iterate through and output pased on the values.
            // Filter out all the boring context lines.
            // If we're not interested in this line just return since it will iterate to the next.
            if maybe_hunk.is_none() { return true }; // Return early.

            //dump_diffline(&line);
            //dump_diffdelta(&delta);
            //dump_diffhunk(&maybe_hunk.unwrap());

            old_path = match delta.old_file().path()
            .and_then(|x| x.to_str()) {
                Some(path) => String::from_str(path),
                None => return false,
            };
            new_path = match delta.new_file().path()
            .and_then(|x| x.to_str()) {
                Some(path) => String::from_str(path),
                None => return false,
            };

            match line.origin() {
                // Additions
                '+' | '>' => {
                    // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                    // TODO: Is it?
                    let line_str = str::from_utf8(line.content()).unwrap_or("");
                    added.push_str(line_str);

                    match state {
                        State::Deletion => {
                            founds.push(Found {
                                filename: old_path.clone(),
                                key: format_key(deleted.clone()),
                                state: FoundState::Deleted,
                                start_position: start_position,
                                line_count: line_count,
                            });
                            deleted = String::new();
                            line_count = 0;
                            start_position = match line.new_lineno() {
                                Some(lineno) => lineno,
                                None => return false, // Can't be a move if deleted.
                            }
                        },
                        State::Addition => {
                            line_count += 1;
                        },
                        State::Other => {
                            line_count = 1;
                            start_position = match line.new_lineno() {
                                Some(lineno) => lineno,
                                None => return false, // Can't be a move if deleted.
                            }
                        },
                    }

                    state = State::Addition;
                    true
                },
                // Deletions
                '-' | '<' => {
                    // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                    // TODO: Is it?
                    let line_str = str::from_utf8(line.content()).unwrap_or("");
                    deleted.push_str(line_str);

                    match state {
                        State::Addition => {
                            founds.push(Found {
                                filename: new_path.clone(),
                                key: format_key(added.clone()),
                                state: FoundState::Added,
                                start_position: start_position,
                                line_count: line_count,
                            });
                            added = String::new();
                            line_count = 0;
                            start_position = match line.old_lineno() {
                                Some(lineno) => lineno,
                                None => return false, // Can't be a move if deleted.
                            }
                        },
                        State::Deletion => {
                            line_count += 1;
                        },
                        State::Other => {
                            line_count = 1;
                            start_position = match line.old_lineno() {
                                Some(lineno) => lineno,
                                None => return false, // Can't be a move if deleted.
                            }
                        },
                    }

                    state = State::Deletion;
                    true
                },
                // Other
                _         => {
                    match state {
                        State::Addition => {
                            founds.push(Found {
                                filename: new_path.clone(),
                                key: format_key(added.clone()),
                                state: FoundState::Added,
                                start_position: start_position,
                                line_count: line_count,
                            });
                            added = String::new();
                        },
                        State::Deletion => {
                            founds.push(Found {
                                filename: old_path.clone(),
                                key: format_key(deleted.clone()),
                                state: FoundState::Deleted,
                                start_position: start_position,
                                line_count: line_count,
                            });
                            deleted = String::new();
                        },
                        _ => (),
                    }

                    state = State::Other;
                    true
                }
            }
        }).ok(); // We don't care if we exit early. That's fine.

        // Grab last one.
        match state {
            State::Addition => {
                if added.len() > 0 {
                    founds.push(Found {
                        filename: new_path.clone(),
                        key: format_key(added.clone()),
                        state: FoundState::Added,
                        start_position: start_position,
                        line_count: line_count,
                    });
                }
            },
            State::Deletion => {
                if deleted.len() > 0 {
                    founds.push(Found {
                        filename: old_path.clone(),
                        key: format_key(deleted.clone()),
                        state: FoundState::Deleted,
                        start_position: start_position,
                        line_count: line_count,
                    });
                }
            },
            _ => (),
        }

        return founds;
    }

    fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
        let old_tree = try!(old.tree());
        let new_tree = try!(new.tree());
        // Build up a diff of the two trees.
        let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

        let founds: Vec<Found> = find_additions_and_deletions(diff);

        let mut moves: Vec<Output> = Vec::new();
        let mut map: HashMap<String, Found> = HashMap::new();

        for f in founds {
            if f.key.len() == 0 { continue; }

            if map.contains_key(&f.key) {
                let q = map.get(&f.key).unwrap();

                // If both states are added, there are no moves.
                // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
                if f.state == q.state {
                    return Ok(Vec::<Output>::new())
                }

                let output: Output;

                match f.state {
                    FoundState::Added => {
                        output = Output {
                            old_filename: q.filename.clone(),
                            new_filename: f.filename.clone(),
                            origin_line: q.start_position,
                            destination_line: f.start_position,
                            num_lines: f.line_count,
                        };
                    },
                    FoundState::Deleted => {
                        output = Output {
                            old_filename: f.filename.clone(),
                            new_filename: q.filename.clone(),
                            origin_line: f.start_position,
                            destination_line: q.start_position,
                            num_lines: f.line_count,
                        };
                    },
                }

                moves.push(output);
            } else {
                map.insert(f.key.clone(), f);
            }
        }

        return Ok(moves);
    }

    #[derive(Debug, RustcEncodable)]
    pub struct OutputSet {
        old: TransitOid,
        old_time: i64, // Seconds from Epoch
        new: TransitOid,
        new_time: i64, // Seconds from Epoch
        outputs: Vec<Output>,
    }

    #[derive(Debug, RustcEncodable)]
    pub struct Output {
        old_filename: String,
        new_filename: String,
        origin_line: u32,
        destination_line: u32,
        num_lines: u32,
    }

    #[derive(Debug, Hash, PartialEq, Eq)]
    pub struct TransitOid(Oid);
    impl fmt::Display for TransitOid {
        fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            fmt::Display::fmt(&self.0, f)
        }
    }

    impl rustc_serialize::Encodable for TransitOid {
        fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
            s.emit_str(&format!("{}", self)[..])
        }
    }
}
<|endoftext|>


<commit_before>extern crate orbclient;

use orbclient::window::EventIter;

use std::time::Instant;

struct Vertex {
    x: f32,
    y: f32
}

impl Vertex {
    pub fn calc_area(&self, v1: &Vertex, v2: &Vertex) -> f32 {

        let x1 = v1.x - self.x;
        let y1 = v1.y - self.y;
        let x2 = v2.x - self.x;
        let y2 = v2.y - self.y;

        (x1 * y2 - x2 * y1)
    }
}

struct RenderContext {
    window: Box<orbclient::Window>,
    scan_buffer: Vec<u32> //TODO(dustin): do i need Vec<32> here? [i32]
}

impl RenderContext {
    pub fn new(width: u32, height: u32, title: &str) -> RenderContext {
        let orb_window = orbclient::Window::new_flags(100, 100, width, height, title, true).unwrap();
        RenderContext{scan_buffer: vec![0; (height * 2) as usize], window: orb_window}
    }

    pub fn events(&mut self) -> EventIter {
        self.window.events()
    }

    pub fn clear(&mut self) {
        self.window.set(orbclient::Color { data: 0xFF000000 });
    }

    pub fn sync(&mut self) {
        self.window.sync();
    }

    pub fn draw_triangle(&mut self, v1: &Vertex, v2: &Vertex, v3: &Vertex) {

        let mut min_vert = &v1;
        let mut mid_vert = &v2;
        let mut max_vert = &v3;

        if max_vert.y < mid_vert.y {
            let tmp = max_vert;
            max_vert = mid_vert;
            mid_vert = tmp;
        }

        if mid_vert.y < min_vert.y {
            let tmp = mid_vert;
            mid_vert = min_vert;
            min_vert = tmp;
        }

        if max_vert.y < mid_vert.y {
            let tmp = max_vert;
            max_vert = mid_vert;
            mid_vert = tmp;
        }

        let area = min_vert.calc_area(&max_vert, &mid_vert);
        let side = if area >= 0f32 { 1 } else { 0 };
        self.convert_triangle(&min_vert, &mid_vert, &max_vert, side);
        self.fill_convex_shape(min_vert.y as u32, max_vert.y as u32);
    }

    fn fill_convex_shape(&mut self, y_min: u32, y_max: u32) {

        for y_idx in y_min..y_max {
            let x_min = self.scan_buffer.get((y_idx * 2) as usize).unwrap().clone();
            let x_max = self.scan_buffer.get((y_idx * 2 + 1) as usize).unwrap().clone();

            for x_idx in x_min..x_max {
                self.window.pixel(x_idx as i32, y_idx as i32, orbclient::Color { data: 0xFFFFFFFF });
            }
        }
    }

    fn convert_triangle(&mut self, min_vert: &Vertex, mid_vert: &Vertex, max_vert: &Vertex, side: i32) {
		self.convert_line(min_vert, max_vert, 0 + side);
		self.convert_line(min_vert, mid_vert, 1 - side);
		self.convert_line(mid_vert, max_vert, 1 - side);
	}

	fn convert_line(&mut self, min_vert: &Vertex, max_vert: &Vertex, side: i32) {
        let start_y = min_vert.y;
        let start_x = min_vert.x;
        let end_y = max_vert.y;
        let end_x = max_vert.x;

        let dist_y = end_y - start_y;
        let dist_x = end_x - start_x;

		if dist_y <= 0f32 {
			return;
		}

		let setp_x = dist_x as f32 / dist_y as f32;
		let mut current_x = start_x;

        for y_coord in start_y as i32..end_y as i32 {
            self.scan_buffer.insert((y_coord * 2 + side) as usize, current_x as u32);
            current_x += setp_x;
        }
	}

}

fn main() {

    let mut render_context = RenderContext::new(500, 400, "pixelcannon");
    let mut start = Instant::now();

    let min_vert = Vertex{x: 100f32, y: 50f32};
    let mid_vert = Vertex{x: 200f32, y: 200f32};
    let max_vert = Vertex{x: 70f32, y: 300f32};

    'event: loop {

        {
            let end = Instant::now();
            let delta = end.duration_since(start);
            let delta_ms = delta.as_secs() * 1000 + (delta.subsec_nanos() as u64)/1000000;
            start = Instant::now();
            // println!("{} ms", delta_ms);

            render_context.clear();
            render_context.draw_triangle(&max_vert, &mid_vert, &min_vert);
            render_context.sync();
        }

        for orbital_event in render_context.events() {
            match orbital_event.to_option() {
                orbclient::EventOption::Quit(_quit_event) => break 'event,
                _ => (),
            };
        }

    }
}
<commit_msg>fixed vector insert bug<commit_after>extern crate orbclient;

use orbclient::window::EventIter;

use std::time::Instant;

struct Vertex {
    x: i32,
    y: i32
}

impl Vertex {
    pub fn calc_double_area(&self, v1: &Vertex, v2: &Vertex) -> i32 {

        let x1 = (v1.x - self.x) as i32;
        let y1 = (v1.y - self.y) as i32;
        let x2 = (v2.x - self.x) as i32;
        let y2 = (v2.y - self.y) as i32;

        (x1 * y2 - x2 * y1)
    }
}

struct RenderContext {
    window: Box<orbclient::Window>,
    scan_buffer: Vec<i32> //TODO(dustin): do i need Vec<32> here? [i32]
}

impl RenderContext {
    pub fn new(width: u32, height: u32, title: &str) -> RenderContext {
        let orb_window = orbclient::Window::new_flags(100, 100, width, height, title, true).unwrap();
        RenderContext{scan_buffer: vec![0; (height * 2) as usize], window: orb_window}
    }

    pub fn events(&mut self) -> EventIter {
        self.window.events()
    }

    pub fn clear(&mut self) {
        self.window.set(orbclient::Color { data: 0xFF000000 });
    }

    pub fn sync(&mut self) {
        self.window.sync();
    }

    pub fn draw_triangle(&mut self, v1: &Vertex, v2: &Vertex, v3: &Vertex) {

        let mut min_vert = &v1;
        let mut mid_vert = &v2;
        let mut max_vert = &v3;

        if max_vert.y < mid_vert.y {
            let tmp = max_vert;
            max_vert = mid_vert;
            mid_vert = tmp;
        }

        if mid_vert.y < min_vert.y {
            let tmp = mid_vert;
            mid_vert = min_vert;
            min_vert = tmp;
        }

        if max_vert.y < mid_vert.y {
            let tmp = max_vert;
            max_vert = mid_vert;
            mid_vert = tmp;
        }

        let area = min_vert.calc_double_area(&max_vert, &mid_vert);
        let side = if area >= 0 { 1 } else { 0 };
        self.convert_triangle(&min_vert, &mid_vert, &max_vert, side);
        self.fill_convex_shape(min_vert.y, max_vert.y);
    }

    fn fill_convex_shape(&mut self, y_min: i32, y_max: i32) {

        for y_idx in y_min..y_max {
            let x_min = self.scan_buffer.get((y_idx * 2) as usize).unwrap().clone();
            let x_max = self.scan_buffer.get((y_idx * 2 + 1) as usize).unwrap().clone();

            for x_idx in x_min..x_max {
                self.window.pixel(x_idx, y_idx, orbclient::Color { data: 0xFFFFFFFF });
            }
        }
    }

    fn convert_triangle(&mut self, min_vert: &Vertex, mid_vert: &Vertex, max_vert: &Vertex, side: i32) {
        self.convert_line(min_vert, max_vert, 0 + side);
        self.convert_line(min_vert, mid_vert, 1 - side);
        self.convert_line(mid_vert, max_vert, 1 - side);
    }

    fn convert_line(&mut self, min_vert: &Vertex, max_vert: &Vertex, side: i32) {
        let start_y = min_vert.y;
        let start_x = min_vert.x;
        let end_y = max_vert.y;
        let end_x = max_vert.x;

        let dist_y = end_y - start_y;
        let dist_x = end_x - start_x;

        if dist_y <= 0 {
            return;
        }

        let step_x = dist_x as f32 / dist_y as f32;
        let mut current_x = start_x as f32;

        for y_coord in start_y..end_y {
            self.scan_buffer[((y_coord * 2 + side) as usize)] = current_x as i32;
            current_x += step_x;
        }
    }

}

fn main() {

    let mut render_context = RenderContext::new(500, 400, "pixelcannon");
    let mut start = Instant::now();

    let min_vert = Vertex{x: 250, y: 0};
    let mid_vert = Vertex{x: 0, y: 200};
    let max_vert = Vertex{x: 500, y: 300};

    'event: loop {

        {
            let end = Instant::now();
            let delta = end.duration_since(start);
            let delta_ms = delta.as_secs() * 1000 + (delta.subsec_nanos() as u64)/1000000;
            start = Instant::now();
            // println!("{} ms", delta_ms);

            render_context.clear();
            render_context.draw_triangle(&min_vert, &mid_vert, &max_vert);
            render_context.sync();
        }

        for orbital_event in render_context.events() {
            match orbital_event.to_option() {
                orbclient::EventOption::Quit(_quit_event) => break 'event,
                _ => (),
            };
        }

    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate rumblebars;
extern crate rustc_serialize;
#[allow(dead_code)] #[macro_use] mod common;

use std::path::Path;
use std::thread;
use std::fs::File;
use std::io::Write;
use std::fs;
use getopts::Options;
use std::env;
use std::process::Command;
use std::path::Display;
use std::process::ExitStatus;
use common::SiteGenResult;

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} init PROJECT_NAME\n       {} run\n       {} compile\n       {} update",
        program, program, program, program);
    print!("{}", opts.usage(&brief));
}

fn copy_default_config_file(filename: &str, home_dir: &Display, root_dir: &str) -> SiteGenResult<()> {
    let new_config = &format!("{}/{}", root_dir, filename);
    try!(fs::copy(format!("{}/.bolts/default_config/{}",home_dir, filename), new_config));
    return Ok(());
}

fn copy_files(project_name: &str) -> SiteGenResult<()> {
    let home_dir = env::home_dir().unwrap();
    let current_dir = env::current_dir().unwrap();
    let root_dir: String = format!("{}/{}/",current_dir.display(),project_name);
    try!(fs::create_dir_all(&format!("{}/config/src",&root_dir)));
    //TODO: stop this, do recursively
    let home_dir_display: Display = home_dir.display();
    try!(copy_default_config_file("config/config.rs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("index.md", &home_dir_display, &root_dir));
    try!(copy_default_config_file(".gitignore", &home_dir_display, &root_dir));
    try!(copy_default_config_file("default_layout.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("header.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("footer.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("config/src/main.rs", &home_dir_display, &root_dir));

    let cargo_info: String = String::from(format!("{}{}{}","[package]
name = \"",&project_name,"\"
version = \"0.0.0\"

[dependencies.bolts]
git = \"https://github.com/ryanr1230/bolts.git\""));

    let mut cargo_file = try!(File::create(format!("{}{}",&root_dir, "/config/Cargo.toml")));
    cargo_file.write(cargo_info.as_ref()).unwrap();
    return Ok(());
}

fn cd_into(path: &Path) -> SiteGenResult<()> {
    let mut current_dir = env::current_dir().unwrap().clone();
    current_dir.push(path);
    try!(env::set_current_dir(current_dir.as_path()));
    Ok(())
}

fn update() -> SiteGenResult<()> {
    try!(cd_into(Path::new("config")));
    let updating_status: ExitStatus = Command::new("cargo").arg("update").arg("-p").arg("bolts").status().unwrap();
    if !updating_status.success() {
        panic!("Couldn't update bolts for configuration runner");
    }
    try!(cd_into(Path::new("..")));
    Ok(())
}

fn compile() -> SiteGenResult<()> {
    try!(cd_into(Path::new("config")));
    let compiling_status: ExitStatus = Command::new("cargo").arg("build").status().unwrap();
    if !compiling_status.success() {
        panic!("Couldn't compile configuration runner");
    }
    try!(cd_into(Path::new("..")));
    Ok(())
}

#[allow(unreachable_code)]
fn run_site_gen() -> SiteGenResult<()> {
    try!(compile());
    let current_dir_buf = env::current_dir().unwrap();
    let project_name = current_dir_buf.file_name().unwrap().to_str().unwrap();
    loop {
        let running_status: ExitStatus =
            Command::new(format!("{}/config/target/debug/{}", current_dir_buf.display(), project_name))
                .status().unwrap();
        if !running_status.success() {
            println!("Running exited with: {}", running_status);
        } else {
            println!("Generated site");
        }
        thread::sleep_ms(4000);
    }
    return Ok(());
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let command: &str = &args[1].clone();
    match command {
        "init" => copy_files(&args[2].clone()).unwrap(),
        "run" => run_site_gen().unwrap(),
        "compile" => compile().unwrap(),
        "update" => update().unwrap(),
        _ => print_usage(&program, opts),
    }
}
<commit_msg>allow different executable names from project (in case yours includes periods or something<commit_after>extern crate getopts;
extern crate rumblebars;
extern crate rustc_serialize;
#[allow(dead_code)] #[macro_use] mod common;

use std::path::Path;
use std::thread;
use std::fs::File;
use std::io::Write;
use std::fs;
use getopts::Options;
use std::env;
use std::process::Command;
use std::path::Display;
use std::process::ExitStatus;
use common::SiteGenResult;

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} init PROJECT_NAME\n       {} run\n       {} compile\n       {} update",
        program, program, program, program);
    print!("{}", opts.usage(&brief));
}

fn copy_default_config_file(filename: &str, home_dir: &Display, root_dir: &str) -> SiteGenResult<()> {
    let new_config = &format!("{}/{}", root_dir, filename);
    try!(fs::copy(format!("{}/.bolts/default_config/{}",home_dir, filename), new_config));
    return Ok(());
}

fn copy_files(project_name: &str) -> SiteGenResult<()> {
    let home_dir = env::home_dir().unwrap();
    let current_dir = env::current_dir().unwrap();
    let root_dir: String = format!("{}/{}/",current_dir.display(),project_name);
    try!(fs::create_dir_all(&format!("{}/config/src",&root_dir)));
    //TODO: stop this, do recursively
    let home_dir_display: Display = home_dir.display();
    try!(copy_default_config_file("config/config.rs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("index.md", &home_dir_display, &root_dir));
    try!(copy_default_config_file(".gitignore", &home_dir_display, &root_dir));
    try!(copy_default_config_file("default_layout.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("header.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("footer.hbs", &home_dir_display, &root_dir));
    try!(copy_default_config_file("config/src/main.rs", &home_dir_display, &root_dir));

    let cargo_info: String = String::from(format!("{}{}{}","[package]
name = \"",&project_name,"\"
version = \"0.0.0\"

[dependencies.bolts]
git = \"https://github.com/ryanr1230/bolts.git\""));

    let mut cargo_file = try!(File::create(format!("{}{}",&root_dir, "/config/Cargo.toml")));
    cargo_file.write(cargo_info.as_ref()).unwrap();
    return Ok(());
}

fn cd_into(path: &Path) -> SiteGenResult<()> {
    let mut current_dir = env::current_dir().unwrap().clone();
    current_dir.push(path);
    try!(env::set_current_dir(current_dir.as_path()));
    Ok(())
}

fn update() -> SiteGenResult<()> {
    try!(cd_into(Path::new("config")));
    let updating_status: ExitStatus = Command::new("cargo").arg("update").arg("-p").arg("bolts").status().unwrap();
    if !updating_status.success() {
        panic!("Couldn't update bolts for configuration runner");
    }
    try!(cd_into(Path::new("..")));
    Ok(())
}

fn compile() -> SiteGenResult<()> {
    try!(cd_into(Path::new("config")));
    let compiling_status: ExitStatus = Command::new("cargo").arg("build").status().unwrap();
    if !compiling_status.success() {
        panic!("Couldn't compile configuration runner");
    }
    try!(cd_into(Path::new("..")));
    Ok(())
}

#[allow(unreachable_code)]
fn run_site_gen(exec_name: Option<String>) -> SiteGenResult<()> {
    try!(compile());
    let current_dir_buf = env::current_dir().unwrap();
    let project_name = current_dir_buf.file_name().unwrap().to_str().unwrap();
    loop {
        let executable = match exec_name.clone() {
            None => String::from(project_name),
            Some(string) => string,
        };
        let running_status: ExitStatus =
            Command::new(format!("{}/config/target/debug/{}", current_dir_buf.display(), executable))
                .status().unwrap();
        if !running_status.success() {
            println!("Running exited with: {}", running_status);
        } else {
            println!("Generated site");
        }
        thread::sleep_ms(4000);
    }
    return Ok(());
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let command: &str = &args[1].clone();
    let exec_name: Option<String> = match args.len() {
        i if i > 2 => Some(args[2].clone()),
        i => None,
    };
    match command {
        "init" => copy_files(&args[2].clone()).unwrap(),
        "run" => run_site_gen(exec_name).unwrap(),
        "compile" => compile().unwrap(),
        "update" => update().unwrap(),
        _ => print_usage(&program, opts),
    }
}
<|endoftext|>


<commit_before>extern crate websocat;

extern crate futures;
extern crate tokio_core;
extern crate tokio_stdin_stdout;

extern crate env_logger;

#[macro_use]
extern crate structopt;

use structopt::StructOpt;

use tokio_core::reactor::{Core};
use websocat::{spec, WebsocatConfiguration, Options};

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

#[derive(StructOpt, Debug)]
#[structopt(after_help = "
Basic examples:
  Connect stdin/stdout to a websocket:
    websocat - ws://echo.websocket.org/

  Listen websocket and redirect it to a TCP port:
    websocat ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678

  See more examples with the --long-help option

Short list of specifiers (see --long-help):
  ws:// wss:// - inetd: ws-listen: inetd-ws: tcp: tcp-l: ws-c:
  autoreconnect: reuse: mirror: threadedstdio: clogged:
  literal: literalreply: assert: udp-connect: open-async:
  readfile: writefile: open-fd: unix-connect: unix-listen:
  unix-dgram: abstract-connect: abstract-listen:
  sh-c:
")]
struct Opt {
    /// First, listening/connecting specifier. See --long-help for info about specifiers.
    s1: String,
    /// Second, connecting specifier
    s2: String,

    #[structopt(short = "u", long = "unidirectional", help="Inhibit copying data from right specifier to left")]
    unidirectional: bool,
    #[structopt(short = "U", long = "unidirectional-reverse", help="Inhibit copying data from left specifier to right")]
    unidirectional_reverse: bool,

    #[structopt(short = "t", long = "text", help="Send text WebSocket messages instead of binary")]
    websocket_text_mode: bool,

    #[structopt(long="oneshot", help="Serve only once")]
    oneshot: bool,

    #[structopt(long="long-help", help="Show full help aboput specifiers and examples")]
    longhelp: bool,

    #[structopt(long="dump-spec", help="Instead of running, dump the specifiers representation to stdout")]
    dumpspec: bool,

    #[structopt(long="protocol", help="Specify Sec-WebSocket-Protocol: header")]
    websocket_protocol: Option<String>,

    #[structopt(long="udp-oneshot", help="udp-listen: replies only one packet per client")]
    udp_oneshot_mode: bool,

    #[structopt(long="unlink", help="Unlink listening UNIX socket before binding to it")]
    unlink_unix_socket:bool,
}

fn longhelp() {
    println!("(see also usual --help message)

Full list of specifiers:
  `-` -- Stdin/stdout
    Read input from console, print to console.
    Can be specified only one time.
    Aliases: `stdio:`, `inetd:`

    `inetd:` also disables logging to stderr.

    Example: like `cat(1)`.
      websocat - -

    Example: for inetd mode
      websocat inetd: literal:$'Hello, world.\n'

  `ws://<url>`, `wss://<url>` -- WebSocket client
    Example: forward port 4554 to a websocket
      websocat tcp-l:127.0.0.1:4554 wss://127.0.0.1/some_websocket

  `ws-listen:<spec>` - Listen for websocket connections
    A combining specifier, but given IPv4 address as argument auto-inserts `tcp-l:`
    Aliases: `listen-ws:` `ws-l:` `l-ws:`

    Example:
        websocat ws-l:127.0.0.1:8808 -

    Example: the same, but more verbose:
        websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-

  `inetd-ws:` - Alias of `ws-l:inetd:`

    Example of inetd.conf line:
      1234 stream tcp nowait myuser  /opt/websocat websocat inetd-ws: tcp:127.0.0.1:22

  `tcp:<hostport>` - connect to specified TCP host and port
    Aliases: `tcp-connect:`,`connect-tcp:`,`c-tcp:`,`tcp-c:`

    Example: like netcat
      websocat - tcp:127.0.0.1:22

    Example: IPv6
      websocat - tcp:[::1]:22

  `tcp-l:<hostport>` - listen TCP port on specified address
    Aliases: `l-tcp:`  `tcp-listen:` `listen-tcp:`

    Example: echo server
      websocat tcp-l:0.0.0.0:1441 mirror:

  `sh-c:<command line>` - start subprocess though 'sh'
    On Unix-like it allows executing command lines.

  `udp:<hostport>` - send and receive packets to specified UDP socket
    Aliases: `udp-connect:` `connect-udp:` `c-udp:` `udp-c:`

  `udp-listen:<hostport>` - bind to socket on host and port
    Aliases: `udp-l:`, `l-udp:`, `listen-udp:`

    Note that it is not a multiconnect specifier: entire lifecycle
    of the UDP socket is the same connection.

    Packets get sent to the most recent seen peer.
    If no peers are seen yet, it waits for the first packet.

    File a feature request on Github if you want proper DNS-like request-reply UDP mode here.

  `ws-connect:<spec>` - low-level WebSocket connector
    A combining specifier. Underlying specifier is should be after the colon.
    URL and Host: header being sent are independent from underlying specifier
    Aliases: `ws-c:` `c-ws:` `connect-ws:`

    Example:
      websocat - ws-c:tcp:127.0.0.1:8808

  `autoreconnect:<spec>` - Auto-reconnector
    Re-establish underlying specifier on any error or EOF

    Example: keep connecting to the port or spin 100% CPU trying if it is closed.
      websocat - autoreconnect:tcp:127.0.0.1:5445

    TODO: implement timeouts

  `reuse:<spec>` - Reuse one connection for serving multiple clients
    Better suited for unidirectional connections

    Example (unreliable): don't disconnect SSH when websocket reconnects
      websocat ws-l:[::]:8088 reuse:tcp:127.0.0.1:22

  `threadedstdio:` - Stdin/stdout, spawning a thread
    Like `-`, but forces threaded mode instead of async mode
    Use when standard input is not `epoll(7)`-able.
    Replaces `-` when `no_unix_stdio` Cargo feature is activated

  `mirror:` - Simply copy output to input

  `open-async:<path>` - Open file for read and write and use it like a socket
    Not for regular files, see readfile: and writefile: instead.

    Example:
      websocat - open-async:/dev/null

  `open-fd:<number>` - Use specified file descriptor like a socket

  `unix-connect:<path>` - Connect to UNIX socket
    Aliases: `unix:`, `connect-unix:`, `unix-c:`, `c-unix:`

  `unix-listen:<path>` - Listen for connections on a UNIX socket
    Aliases: `unix-l:`, `listen-unix:`, `l-unix:`

  `unix-dgram:<path>:<path>` - Send packets to one path, receive from the other

  `abstract-connect:<string>` - Connect to Linux abstract-namespaced socket
    Aliases: `abstract-c:`, `connect-abstract:`, `c-abstract:`, `abstract:`

  `abstract-listen:<path>` - Listen for connections on Linux abstract-namespaced socket
    Aliases: `abstract-l:`, `listen-abstract:`, `l-abstract:`

  `readfile:<path>` - synchronously read files
    Blocking on operations with the file pauses the whole process

    Example:
      websocat ws-l:127.0.0.1:8000 readfile:hello.json

  `write:<path>` - synchronously write files
    Blocking on operations with the file pauses the whole process
    Files are opened in overwrite mode.

    Example:
      websocat ws-l:127.0.0.1:8000 reuse:writefile:log.txt

  `clogged:` - Do nothing
    Don't read or write any bytes. Keep connections hanging.

  `literal:<string>` - Output a string, discard input.
    Ignore all input, use specified string as output.

  `literalreply:<string>` - Reply with this string for each input packet

  `assert:<string>` - Check the input.
    Read entire input and panic the program if the input is not equal
    to the specified string.

  TODO:
  `exec:`, `seqpacket:`

More examples:
  Wacky mode:
    websocat ws-l:ws-l:ws-c:- tcp:127.0.0.1:5678

    Connect to a websocket using stdin/stdout as a transport,
    then accept a websocket connection over this previous websocket as a transport,
    then connect to a websocket using previous step as a transport,
    then forward resulting connection to the TCP port.

    (Excercise to the reader: manage to actually connect to it).
");
}

fn run() -> Result<()> {
    if std::env::args().nth(1).unwrap_or_default() == "--long-help" {
        longhelp();
        return Ok(());
    }

    let cmd = Opt::from_args();

    if cmd.longhelp {
        longhelp();
        return Ok(());
    }

    if false
    //    || cmd.oneshot
    {
        Err("This mode is not implemented")?
    }

    let opts = {
        macro_rules! opts {
            ($($o:ident)*) => {
                Options {
                    $($o : cmd.$o,)*
                }
            }
        }
        opts!(
            websocket_text_mode
            websocket_protocol
            udp_oneshot_mode
            unidirectional
            unidirectional_reverse
            oneshot
            unlink_unix_socket
        )
    };

    let s1 = spec(&cmd.s1)?;
    let s2 = spec(&cmd.s2)?;

    let mut websocat = WebsocatConfiguration { opts, s1, s2 };

    while let Some(concern) = websocat.get_concern() {
        use websocat::ConfigurationConcern::*;
        if concern == StdinToStdout {
            if cmd.dumpspec {
                println!("cat mode");
                return Ok(())
            }

            // Degenerate mode: just copy stdin to stdout and call it a day
            ::std::io::copy(&mut ::std::io::stdin(), &mut ::std::io::stdout())?;
            return Ok(())
        }

        if concern == DegenerateMode {
            if cmd.dumpspec {
                println!("noop");
            }
            return Ok(())
        }

        if concern == StdioConflict {
            Err("Too many usages of stdin/stdout")?;
        }

        if concern == NeedsStdioReuser {
            eprintln!("Warning: replies on stdio get directed at random connected client");
            websocat = websocat.auto_install_reuser();
            continue;
        }

        if concern == NeedsStdioReuser2 {
            websocat = websocat.auto_install_reuser();
            continue;
        }

        if concern == MultipleReusers {
            eprintln!("Specifier dump: {:?} {:?}", websocat.s1, websocat.s2);
            Err("Multiple reusers is not allowed")?;
        }
        break;
    }

    if cmd.dumpspec {
        println!("{:?}", websocat.s1);
        println!("{:?}", websocat.s2);
        println!("{:?}", websocat.opts);
        return Ok(())
    }

    let mut core = Core::new()?;

    let prog = websocat.serve(core.handle(), std::rc::Rc::new(|e| {
        eprintln!("websocat: {}", e);
    }));
    core.run(prog).map_err(|()|"error running".to_string())?;
    Ok(())
}

fn main() {
    env_logger::init();
    let r = run();

    if let Err(e) = r {
        eprintln!("websocat: {}", e);
        ::std::process::exit(1);
    }
}
<commit_msg>Add argument for future exec: specifier<commit_after>extern crate websocat;

extern crate futures;
extern crate tokio_core;
extern crate tokio_stdin_stdout;

extern crate env_logger;

#[macro_use]
extern crate structopt;

use structopt::StructOpt;

use tokio_core::reactor::{Core};
use websocat::{spec, WebsocatConfiguration, Options};

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

#[derive(StructOpt, Debug)]
#[structopt(after_help = "
Basic examples:
  Connect stdin/stdout to a websocket:
    websocat - ws://echo.websocket.org/

  Listen websocket and redirect it to a TCP port:
    websocat ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678

  See more examples with the --long-help option

Short list of specifiers (see --long-help):
  ws:// wss:// - inetd: ws-listen: inetd-ws: tcp: tcp-l: ws-c:
  autoreconnect: reuse: mirror: threadedstdio: clogged:
  literal: literalreply: assert: udp-connect: open-async:
  readfile: writefile: open-fd: unix-connect: unix-listen:
  unix-dgram: abstract-connect: abstract-listen:
  sh-c:
")]
struct Opt {
    /// First, listening/connecting specifier. See --long-help for info about specifiers.
    s1: String,
    /// Second, connecting specifier
    s2: String,

    #[structopt(short = "u", long = "unidirectional", help="Inhibit copying data from right specifier to left")]
    unidirectional: bool,
    #[structopt(short = "U", long = "unidirectional-reverse", help="Inhibit copying data from left specifier to right")]
    unidirectional_reverse: bool,

    #[structopt(short = "t", long = "text", help="Send text WebSocket messages instead of binary")]
    websocket_text_mode: bool,

    #[structopt(long="oneshot", help="Serve only once")]
    oneshot: bool,

    #[structopt(long="long-help", help="Show full help aboput specifiers and examples")]
    longhelp: bool,

    #[structopt(long="dump-spec", help="Instead of running, dump the specifiers representation to stdout")]
    dumpspec: bool,

    #[structopt(long="protocol", help="Specify Sec-WebSocket-Protocol: header")]
    websocket_protocol: Option<String>,

    #[structopt(long="udp-oneshot", help="udp-listen: replies only one packet per client")]
    udp_oneshot_mode: bool,

    #[structopt(long="unlink", help="Unlink listening UNIX socket before binding to it")]
    unlink_unix_socket:bool,

    #[structopt(long="exec-args", help="Arguments for the `exec:` specifier. Specify them at the end or terminate with --.")]
    exec_args: Vec<String>,
}

fn longhelp() {
    println!("(see also usual --help message)

Full list of specifiers:
  `-` -- Stdin/stdout
    Read input from console, print to console.
    Can be specified only one time.
    Aliases: `stdio:`, `inetd:`

    `inetd:` also disables logging to stderr.

    Example: like `cat(1)`.
      websocat - -

    Example: for inetd mode
      websocat inetd: literal:$'Hello, world.\n'

  `ws://<url>`, `wss://<url>` -- WebSocket client
    Example: forward port 4554 to a websocket
      websocat tcp-l:127.0.0.1:4554 wss://127.0.0.1/some_websocket

  `ws-listen:<spec>` - Listen for websocket connections
    A combining specifier, but given IPv4 address as argument auto-inserts `tcp-l:`
    Aliases: `listen-ws:` `ws-l:` `l-ws:`

    Example:
        websocat ws-l:127.0.0.1:8808 -

    Example: the same, but more verbose:
        websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-

  `inetd-ws:` - Alias of `ws-l:inetd:`

    Example of inetd.conf line:
      1234 stream tcp nowait myuser  /opt/websocat websocat inetd-ws: tcp:127.0.0.1:22

  `tcp:<hostport>` - connect to specified TCP host and port
    Aliases: `tcp-connect:`,`connect-tcp:`,`c-tcp:`,`tcp-c:`

    Example: like netcat
      websocat - tcp:127.0.0.1:22

    Example: IPv6
      websocat - tcp:[::1]:22

  `tcp-l:<hostport>` - listen TCP port on specified address
    Aliases: `l-tcp:`  `tcp-listen:` `listen-tcp:`

    Example: echo server
      websocat tcp-l:0.0.0.0:1441 mirror:

  `sh-c:<command line>` - start subprocess though 'sh'
    On Unix-like it allows executing command lines.

  `udp:<hostport>` - send and receive packets to specified UDP socket
    Aliases: `udp-connect:` `connect-udp:` `c-udp:` `udp-c:`

  `udp-listen:<hostport>` - bind to socket on host and port
    Aliases: `udp-l:`, `l-udp:`, `listen-udp:`

    Note that it is not a multiconnect specifier: entire lifecycle
    of the UDP socket is the same connection.

    Packets get sent to the most recent seen peer.
    If no peers are seen yet, it waits for the first packet.

    File a feature request on Github if you want proper DNS-like request-reply UDP mode here.

  `ws-connect:<spec>` - low-level WebSocket connector
    A combining specifier. Underlying specifier is should be after the colon.
    URL and Host: header being sent are independent from underlying specifier
    Aliases: `ws-c:` `c-ws:` `connect-ws:`

    Example:
      websocat - ws-c:tcp:127.0.0.1:8808

  `autoreconnect:<spec>` - Auto-reconnector
    Re-establish underlying specifier on any error or EOF

    Example: keep connecting to the port or spin 100% CPU trying if it is closed.
      websocat - autoreconnect:tcp:127.0.0.1:5445

    TODO: implement timeouts

  `reuse:<spec>` - Reuse one connection for serving multiple clients
    Better suited for unidirectional connections

    Example (unreliable): don't disconnect SSH when websocket reconnects
      websocat ws-l:[::]:8088 reuse:tcp:127.0.0.1:22

  `threadedstdio:` - Stdin/stdout, spawning a thread
    Like `-`, but forces threaded mode instead of async mode
    Use when standard input is not `epoll(7)`-able.
    Replaces `-` when `no_unix_stdio` Cargo feature is activated

  `mirror:` - Simply copy output to input

  `open-async:<path>` - Open file for read and write and use it like a socket
    Not for regular files, see readfile: and writefile: instead.

    Example:
      websocat - open-async:/dev/null

  `open-fd:<number>` - Use specified file descriptor like a socket

  `unix-connect:<path>` - Connect to UNIX socket
    Aliases: `unix:`, `connect-unix:`, `unix-c:`, `c-unix:`

  `unix-listen:<path>` - Listen for connections on a UNIX socket
    Aliases: `unix-l:`, `listen-unix:`, `l-unix:`

  `unix-dgram:<path>:<path>` - Send packets to one path, receive from the other

  `abstract-connect:<string>` - Connect to Linux abstract-namespaced socket
    Aliases: `abstract-c:`, `connect-abstract:`, `c-abstract:`, `abstract:`

  `abstract-listen:<path>` - Listen for connections on Linux abstract-namespaced socket
    Aliases: `abstract-l:`, `listen-abstract:`, `l-abstract:`

  `readfile:<path>` - synchronously read files
    Blocking on operations with the file pauses the whole process

    Example:
      websocat ws-l:127.0.0.1:8000 readfile:hello.json

  `write:<path>` - synchronously write files
    Blocking on operations with the file pauses the whole process
    Files are opened in overwrite mode.

    Example:
      websocat ws-l:127.0.0.1:8000 reuse:writefile:log.txt

  `clogged:` - Do nothing
    Don't read or write any bytes. Keep connections hanging.

  `literal:<string>` - Output a string, discard input.
    Ignore all input, use specified string as output.

  `literalreply:<string>` - Reply with this string for each input packet

  `assert:<string>` - Check the input.
    Read entire input and panic the program if the input is not equal
    to the specified string.

  TODO:
  `exec:`, `seqpacket:`

More examples:
  Wacky mode:
    websocat ws-l:ws-l:ws-c:- tcp:127.0.0.1:5678

    Connect to a websocket using stdin/stdout as a transport,
    then accept a websocket connection over this previous websocket as a transport,
    then connect to a websocket using previous step as a transport,
    then forward resulting connection to the TCP port.

    (Excercise to the reader: manage to actually connect to it).
");
}

fn run() -> Result<()> {
    if std::env::args().nth(1).unwrap_or_default() == "--long-help" {
        longhelp();
        return Ok(());
    }

    let cmd = Opt::from_args();

    if cmd.longhelp {
        longhelp();
        return Ok(());
    }

    if false
    //    || cmd.oneshot
    {
        Err("This mode is not implemented")?
    }

    let opts = {
        macro_rules! opts {
            ($($o:ident)*) => {
                Options {
                    $($o : cmd.$o,)*
                }
            }
        }
        opts!(
            websocket_text_mode
            websocket_protocol
            udp_oneshot_mode
            unidirectional
            unidirectional_reverse
            oneshot
            unlink_unix_socket
        )
    };

    let s1 = spec(&cmd.s1)?;
    let s2 = spec(&cmd.s2)?;

    let mut websocat = WebsocatConfiguration { opts, s1, s2 };

    while let Some(concern) = websocat.get_concern() {
        use websocat::ConfigurationConcern::*;
        if concern == StdinToStdout {
            if cmd.dumpspec {
                println!("cat mode");
                return Ok(())
            }

            // Degenerate mode: just copy stdin to stdout and call it a day
            ::std::io::copy(&mut ::std::io::stdin(), &mut ::std::io::stdout())?;
            return Ok(())
        }

        if concern == DegenerateMode {
            if cmd.dumpspec {
                println!("noop");
            }
            return Ok(())
        }

        if concern == StdioConflict {
            Err("Too many usages of stdin/stdout")?;
        }

        if concern == NeedsStdioReuser {
            eprintln!("Warning: replies on stdio get directed at random connected client");
            websocat = websocat.auto_install_reuser();
            continue;
        }

        if concern == NeedsStdioReuser2 {
            websocat = websocat.auto_install_reuser();
            continue;
        }

        if concern == MultipleReusers {
            eprintln!("Specifier dump: {:?} {:?}", websocat.s1, websocat.s2);
            Err("Multiple reusers is not allowed")?;
        }
        break;
    }

    if cmd.dumpspec {
        println!("{:?}", websocat.s1);
        println!("{:?}", websocat.s2);
        println!("{:?}", websocat.opts);
        return Ok(())
    }

    let mut core = Core::new()?;

    let prog = websocat.serve(core.handle(), std::rc::Rc::new(|e| {
        eprintln!("websocat: {}", e);
    }));
    core.run(prog).map_err(|()|"error running".to_string())?;
    Ok(())
}

fn main() {
    env_logger::init();
    let r = run();

    if let Err(e) = r {
        eprintln!("websocat: {}", e);
        ::std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;
extern crate syn;
use syn::*;

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    for attr in attrs {
        if let syn::MetaItem::Word(ref ident) = attr.value {
            if ident == "no_mangle" {
                return true;
            }
        }
    }
    false
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        _ => l
    }
}

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

fn map_ty(ty: &Ty) -> String {
    match ty {
        &Ty::Path(_, ref p) => {
            map_path(p)
        },
        &Ty::Ptr(ref p) => {
            format!("{}*", map_ty(&p.ty))
        },
        &Ty::Rptr(_, ref mut_ty) => {
            format!("{}*", map_mut_ty(mut_ty))
        }
        _ => format!("unknown {:?}", ty)
    }

}

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

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 main() {
    let p = env::args().nth(1).unwrap();
    let mut s = String::new();
    let mut f = File::open(p).unwrap();
    f.read_to_string(&mut s).unwrap();
    let krate = syn::parse_crate(&s).unwrap();
    for item in krate.items {
        match item.node {
            ItemKind::Fn(decl, ..) => {
                if has_no_mangle(&item.attrs) {
                    println!("{} {}({})", map_return_type(&decl.output), item.ident, decl.inputs.iter().map(map_arg).collect::<Vec<_>>().join(", "));
                }
            }
            _ => {}
        }
    }

}
<commit_msg>Map f32<commit_after>use std::env;
use std::fs::File;
use std::io::Read;
extern crate syn;
use syn::*;

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    for attr in attrs {
        if let syn::MetaItem::Word(ref ident) = attr.value {
            if ident == "no_mangle" {
                return true;
            }
        }
    }
    false
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "f32" => "float".to_string(),
        _ => l
    }
}

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

fn map_ty(ty: &Ty) -> String {
    match ty {
        &Ty::Path(_, ref p) => {
            map_path(p)
        },
        &Ty::Ptr(ref p) => {
            format!("{}*", map_ty(&p.ty))
        },
        &Ty::Rptr(_, ref mut_ty) => {
            format!("{}*", map_mut_ty(mut_ty))
        }
        _ => format!("unknown {:?}", ty)
    }

}

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

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 main() {
    let p = env::args().nth(1).unwrap();
    let mut s = String::new();
    let mut f = File::open(p).unwrap();
    f.read_to_string(&mut s).unwrap();
    let krate = syn::parse_crate(&s).unwrap();
    for item in krate.items {
        match item.node {
            ItemKind::Fn(decl, ..) => {
                if has_no_mangle(&item.attrs) {
                    println!("{} {}({})", map_return_type(&decl.output), item.ident, decl.inputs.iter().map(map_arg).collect::<Vec<_>>().join(", "));
                }
            }
            _ => {}
        }
    }

}
<|endoftext|>