<commit_before>#[macro_use]
extern crate websocat;

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

extern crate env_logger;

#[cfg(feature = "openssl-probe")]
extern crate openssl_probe;

#[macro_use]
extern crate structopt;

use structopt::StructOpt;

use tokio_core::reactor::Core;

use websocat::{Options, SpecifierClass, WebsocatConfiguration1};
use websocat::options::StaticFile;

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

#[derive(StructOpt, Debug)]
#[structopt(
    after_help = "
Basic examples:
  Command-line websocket client:
    websocat ws://echo.websocket.org/

  WebSocket server
    websocat -s 8080

  WebSocket-to-TCP proxy:
    websocat --binary ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678

",
    usage = "websocat ws://URL | wss://URL               (simple client)\n    websocat -s port                            (simple server)\n    websocat [FLAGS] [OPTIONS] <addr1> <addr2>  (advanced mode)"
)]
struct Opt {
    /// In simple mode, WebSocket URL to connect.
    /// In advanced mode first address (there are many kinds of addresses) to use.
    /// See --help=types for info about address types.
    /// If this is an address for listening, it will try serving multiple connections.
    addr1: Option<String>,
    /// In advanced mode, second address to connect.
    /// If this is an address for listening, it will accept only one connection.
    addr2: Option<String>,

    #[structopt(
        short = "u", long = "unidirectional", help = "Inhibit copying data in one direction"
    )]
    unidirectional: bool,
    #[structopt(
        short = "U",
        long = "unidirectional-reverse",
        help = "Inhibit copying data in the other direction (or maybe in both directions if combined with -u)"
    )]
    unidirectional_reverse: bool,

    #[structopt(
        long = "exit-on-eof",
        short = "E",
        help = "Close a data transfer direction if the other one reached EOF"
    )]
    exit_on_eof: bool,

    #[structopt(short = "t", long = "text", help = "Send message to WebSockets as text messages")]
    websocket_text_mode: bool,

    #[structopt(
        short = "b", long = "binary", help = "Send message to WebSockets as binary messages"
    )]
    websocket_binary_mode: bool,

    #[structopt(
        long = "oneshot", help = "Serve only once. Not to be confused with -1 (--one-message)"
    )]
    oneshot: bool,

    #[structopt(
        short = "h",
        long = "help",
        help = "See the help.\n--help=short is the list of easy options and address types\n--help=long lists all options and types (see [A] markers)\n--help=doc also shows longer description and examples."
    )]
    help: Option<String>,

    #[structopt(
        long = "dump-spec",
        help = "[A] 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 = "[A] udp-listen: replies only one packet per client")]
    udp_oneshot_mode: bool,

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

    #[structopt(
        long = "exec-args",
        raw(allow_hyphen_values = r#"true"#),
        help = "[A] Arguments for the `exec:` specifier. Must be the last option, everything after it gets into the exec args list."
    )]
    exec_args: Vec<String>,

    #[structopt(
        long = "ws-c-uri",
        help = "[A] URI to use for ws-c: overlay",
        default_value = "ws://0.0.0.0/"
    )]
    ws_c_uri: String,

    #[structopt(
        long = "linemode-strip-newlines",
        help = "[A] Don't include trailing \\n or \\r\\n coming from streams in WebSocket messages"
    )]
    linemode_strip_newlines: bool,

    #[structopt(
        long = "--no-line", help = "[A] Don't automatically insert line-to-message transformation"
    )]
    no_auto_linemode: bool,

    #[structopt(long = "origin", help = "Add Origin HTTP header to websocket client request")]
    origin: Option<String>,

    #[structopt(
        long = "header",
        short = "H",
        help = "Add custom HTTP header to websocket client request. Separate header name and value with a colon and optionally a single space. Can be used multiple times.",
        parse(try_from_str = "interpret_custom_header")
    )]
    custom_headers: Vec<(String, Vec<u8>)>,

    #[structopt(long = "websocket-version", help = "Override the Sec-WebSocket-Version value")]
    websocket_version: Option<String>,

    #[structopt(
        long = "no-close", short = "n", help = "Don't send Close message to websocket on EOF"
    )]
    websocket_dont_close: bool,

    #[structopt(
        short = "1",
        long = "one-message",
        help = "Send and/or receive only one message. Use with --no-close and/or -u/-U."
    )]
    one_message: bool,

    #[structopt(
        short = "s",
        long = "server-mode",
        help = "Simple server mode: specify TCP port or addr:port as single argument"
    )]
    server_mode: bool,

    #[structopt(
        long = "no-fixups",
        help = "[A] Don't perform automatic command-line fixups. May destabilize websocat operation. Use --dump-spec without --no-fixups to discover what is being inserted automatically and read the full manual about Websocat internal workings."
    )]
    no_lints: bool,

    #[structopt(
        short = "B",
        long = "buffer-size",
        help = "Maximum message size, in bytes",
        default_value = "65536"
    )]
    buffer_size: usize,

    #[structopt(
        short = "v", parse(from_occurrences), help = "Increase verbosity level to info or further"
    )]
    verbosity: u8,

    #[structopt(short = "q", help = "Suppress all diagnostic messages, except of startup errors")]
    quiet: bool,

    #[structopt(
        long = "queue-len",
        help = "[A] Number of pending queued messages for broadcast reuser",
        default_value = "16"
    )]
    broadcast_queue_len: usize,

    #[structopt(
        short = "S",
        long = "strict",
        help = "strict line/message mode: drop too long messages instead of splitting them, drop incomplete lines."
    )]
    strict_mode: bool,

    #[structopt(
        short = "0", long = "null-terminated", help = "Use \\0 instead of \\n for linemode"
    )]
    linemode_zero_terminated: bool,

    #[structopt(
        long = "restrict-uri",
        help = "When serving a websocket, only accept the given URI, like `/ws`\nThis liberates other URIs for things like serving static files or proxying."
    )]
    restrict_uri: Option<String>,

    #[structopt(
        short = "F",
        long = "static-file",
        help = "Serve a named static file for non-websocket connections.\nArgument syntax: <URI>:<Content-Type>:<file-path>\nArgument example: /index.html:text/html:index.html\nDirectories are not and will not be supported for security reasons.\nCan be specified multiple times.",
        parse(try_from_str = "interpret_static_file")
    )]
    serve_static_files: Vec<StaticFile>,
}

// TODO: make it byte-oriented/OsStr?
fn interpret_custom_header(x: &str) -> Result<(String, Vec<u8>)> {
    let colon = x.find(':');
    let colon = if let Some(colon) = colon {
        colon
    } else {
        Err("Argument to --header must contain `:` character")?
    };
    let hn = &x[0..colon];
    let mut hv = &x[colon + 1..];
    if hv.starts_with(' ') {
        hv = &x[colon + 2..];
    }
    Ok((hn.to_owned(), hv.as_bytes().to_vec()))
}

fn interpret_static_file(x: &str) -> Result<StaticFile> {
    let colon1 = match x.find(':') {
        Some(x) => x,
        None => Err("Argument to --static-file must contain two colons (`:`)")?
    };
    let uri = &x[0..colon1];
    let rest = &x[colon1+1..];
    let colon2 = match rest.find(':') {
        Some(x) => x,
        None => Err("Argument to --static-file must contain two colons (`:`)")?
    };
    let ct = &rest[0..colon2];
    let fp = &rest[colon2+1..];
    if uri.is_empty() || ct.is_empty() || fp.is_empty() {
        Err("Empty URI, content-type or path in --static-file parameter")?
    }
    Ok(StaticFile{
        uri: uri.to_string(),
        content_type: ct.to_string(),
        file: fp.into(),
    })
}

pub mod help;

// Based on https://github.com/rust-clique/clap-verbosity-flag/blob/master/src/lib.rs
mod logging {

    extern crate env_logger;
    extern crate log;

    use self::env_logger::Builder as LoggerBuilder;
    use self::log::Level;

    pub fn setup_env_logger(ll: u8) -> Result<(), Box<::std::error::Error>> {
        if ::std::env::var("RUST_LOG").is_ok() {
            if ll > 0 {
                eprintln!("websocat: RUST_LOG environment variable overrides any -v");
            }
            env_logger::init();
            return Ok(());
        }

        let lf = match ll {
            //0 => Level::Error,
            0 => Level::Warn,
            1 => Level::Info,
            2 => Level::Debug,
            _ => Level::Trace,
        }.to_level_filter();

        LoggerBuilder::new()
            .filter(Some("websocat"), lf)
            .filter(None, Level::Warn.to_level_filter())
            .try_init()?;
        Ok(())
    }

}

fn run() -> Result<()> {
    if std::env::args().nth(1).unwrap_or_default() == "--long-help" {
        help::longhelp();
        return Ok(());
    }
    if vec!["-?", "-h", "--help"].contains(&std::env::args().nth(1).unwrap_or_default().as_str()) {
        help::shorthelp();
        return Ok(());
    }

    let mut cmd = Opt::from_args();

    let mut quiet = cmd.quiet;

    if let Some(h) = cmd.help {
        if &h == "long" || &h == "full" {
            help::longhelp();
            return Ok(());
        } else if &h == "doc" {
            help::dochelp();
            return Ok(());
        }

        help::shorthelp();
        return Ok(());
    }

    let mut recommend_explicit_text_or_bin = false;

    if cmd.websocket_binary_mode && cmd.websocket_text_mode {
        Err("--binary and --text are mutually exclusive")?;
    }
    if !cmd.websocket_binary_mode && !cmd.websocket_text_mode {
        cmd.websocket_text_mode = true;
        recommend_explicit_text_or_bin = true;
    }

    //if !cmd.serve_static_files.is_empty() && cmd.restrict_uri.is_none() {
    //    Err("Specify --static-file is not supported without --restrict-uri")?
    //}

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

    #[cfg(feature = "openssl-probe")]
    {
        openssl_probe::init_ssl_cert_env_vars();
    }

    let mut opts: Options = Default::default();
    {
        macro_rules! opts {
            ($($o:ident)*) => {{
                $(opts.$o = cmd.$o;)*
            }};
        }
        opts!(
            websocket_text_mode
            websocket_protocol
            udp_oneshot_mode
            unidirectional
            unidirectional_reverse
            exit_on_eof
            oneshot
            unlink_unix_socket
            exec_args
            ws_c_uri
            linemode_strip_newlines
            origin
            custom_headers
            websocket_version
            websocket_dont_close
            one_message
            no_auto_linemode
            buffer_size
            linemode_zero_terminated
            restrict_uri
            serve_static_files
        )
    };

    let (s1, s2): (String, String) = match (cmd.addr1, cmd.addr2) {
        (None, None) => {
            help::shorthelp();
            return Err("No URL specified")?;
        }
        (Some(cmds1), Some(cmds2)) => {
            // Advanced mode
            if cmd.server_mode {
                Err("--server and two positional arguments are incompatible.\nBuild server command line without -s option, but with `listen` address types")?
            }
            (cmds1, cmds2)
        }
        (Some(cmds1), None) => {
            // Easy mode
            recommend_explicit_text_or_bin = false;
            if cmd.server_mode {
                if cmds1.contains(':') {
                    if !quiet {
                        eprintln!("Listening on ws://{}/", cmds1);
                    }
                    (format!("ws-l:{}", cmds1), "-".to_string())
                } else {
                    if !quiet {
                        eprintln!("Listening on ws://127.0.0.1:{}/", cmds1);
                    }
                    (format!("ws-l:127.0.0.1:{}", cmds1), "-".to_string())
                }
            } else {
                if !(cmds1.starts_with("ws://") || cmds1.starts_with("wss://")) {
                    if !quiet {
                        eprintln!("Specify ws:// or wss:// URI to connect to a websocket");
                    }
                    Err("Invalid command-line parameters")?;
                }
                ("-".to_string(), cmds1)
            }
        }
        (None, Some(_)) => unreachable!(),
    };

    if opts.websocket_text_mode {
        opts.read_debt_handling = websocat::readdebt::DebtHandling::Warn;
    }
    if cmd.strict_mode {
        opts.read_debt_handling = websocat::readdebt::DebtHandling::DropMessage;
        opts.linemode_strict = true;
    }

    let websocat1 = WebsocatConfiguration1 {
        opts,
        addr1: s1,
        addr2: s2,
    };
    let mut websocat2 = websocat1.parse1()?;

    if websocat2.inetd_mode() {
        quiet = true;
    }

    if !quiet && recommend_explicit_text_or_bin {
        eprintln!("It is recommended to either set --binary or --text explicitly");
    }
    if !quiet {
        logging::setup_env_logger(cmd.verbosity)?;
    }

    if !cmd.no_lints {
        websocat2.lint_and_fixup(&move |e: &str| {
            if !quiet {
                eprintln!("{}", e);
            }
        })?;
    }
    let websocat = websocat2.parse2()?;

    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(move |e| {
            if !quiet {
                eprintln!("websocat: {}", e);
            }
        }),
    );
    core.run(prog).map_err(|()| "error running".to_string())?;
    Ok(())
}

fn main() {
    let r = run();

    if let Err(e) = r {
        eprintln!("websocat: {}", e);
        ::std::process::exit(1);
    }
}
<commit_msg>Prepend "websocat: " for warnings<commit_after>#[macro_use]
extern crate websocat;

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

extern crate env_logger;

#[cfg(feature = "openssl-probe")]
extern crate openssl_probe;

#[macro_use]
extern crate structopt;

use structopt::StructOpt;

use tokio_core::reactor::Core;

use websocat::{Options, SpecifierClass, WebsocatConfiguration1};
use websocat::options::StaticFile;

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

#[derive(StructOpt, Debug)]
#[structopt(
    after_help = "
Basic examples:
  Command-line websocket client:
    websocat ws://echo.websocket.org/

  WebSocket server
    websocat -s 8080

  WebSocket-to-TCP proxy:
    websocat --binary ws-l:127.0.0.1:8080 tcp:127.0.0.1:5678

",
    usage = "websocat ws://URL | wss://URL               (simple client)\n    websocat -s port                            (simple server)\n    websocat [FLAGS] [OPTIONS] <addr1> <addr2>  (advanced mode)"
)]
struct Opt {
    /// In simple mode, WebSocket URL to connect.
    /// In advanced mode first address (there are many kinds of addresses) to use.
    /// See --help=types for info about address types.
    /// If this is an address for listening, it will try serving multiple connections.
    addr1: Option<String>,
    /// In advanced mode, second address to connect.
    /// If this is an address for listening, it will accept only one connection.
    addr2: Option<String>,

    #[structopt(
        short = "u", long = "unidirectional", help = "Inhibit copying data in one direction"
    )]
    unidirectional: bool,
    #[structopt(
        short = "U",
        long = "unidirectional-reverse",
        help = "Inhibit copying data in the other direction (or maybe in both directions if combined with -u)"
    )]
    unidirectional_reverse: bool,

    #[structopt(
        long = "exit-on-eof",
        short = "E",
        help = "Close a data transfer direction if the other one reached EOF"
    )]
    exit_on_eof: bool,

    #[structopt(short = "t", long = "text", help = "Send message to WebSockets as text messages")]
    websocket_text_mode: bool,

    #[structopt(
        short = "b", long = "binary", help = "Send message to WebSockets as binary messages"
    )]
    websocket_binary_mode: bool,

    #[structopt(
        long = "oneshot", help = "Serve only once. Not to be confused with -1 (--one-message)"
    )]
    oneshot: bool,

    #[structopt(
        short = "h",
        long = "help",
        help = "See the help.\n--help=short is the list of easy options and address types\n--help=long lists all options and types (see [A] markers)\n--help=doc also shows longer description and examples."
    )]
    help: Option<String>,

    #[structopt(
        long = "dump-spec",
        help = "[A] 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 = "[A] udp-listen: replies only one packet per client")]
    udp_oneshot_mode: bool,

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

    #[structopt(
        long = "exec-args",
        raw(allow_hyphen_values = r#"true"#),
        help = "[A] Arguments for the `exec:` specifier. Must be the last option, everything after it gets into the exec args list."
    )]
    exec_args: Vec<String>,

    #[structopt(
        long = "ws-c-uri",
        help = "[A] URI to use for ws-c: overlay",
        default_value = "ws://0.0.0.0/"
    )]
    ws_c_uri: String,

    #[structopt(
        long = "linemode-strip-newlines",
        help = "[A] Don't include trailing \\n or \\r\\n coming from streams in WebSocket messages"
    )]
    linemode_strip_newlines: bool,

    #[structopt(
        long = "--no-line", help = "[A] Don't automatically insert line-to-message transformation"
    )]
    no_auto_linemode: bool,

    #[structopt(long = "origin", help = "Add Origin HTTP header to websocket client request")]
    origin: Option<String>,

    #[structopt(
        long = "header",
        short = "H",
        help = "Add custom HTTP header to websocket client request. Separate header name and value with a colon and optionally a single space. Can be used multiple times.",
        parse(try_from_str = "interpret_custom_header")
    )]
    custom_headers: Vec<(String, Vec<u8>)>,

    #[structopt(long = "websocket-version", help = "Override the Sec-WebSocket-Version value")]
    websocket_version: Option<String>,

    #[structopt(
        long = "no-close", short = "n", help = "Don't send Close message to websocket on EOF"
    )]
    websocket_dont_close: bool,

    #[structopt(
        short = "1",
        long = "one-message",
        help = "Send and/or receive only one message. Use with --no-close and/or -u/-U."
    )]
    one_message: bool,

    #[structopt(
        short = "s",
        long = "server-mode",
        help = "Simple server mode: specify TCP port or addr:port as single argument"
    )]
    server_mode: bool,

    #[structopt(
        long = "no-fixups",
        help = "[A] Don't perform automatic command-line fixups. May destabilize websocat operation. Use --dump-spec without --no-fixups to discover what is being inserted automatically and read the full manual about Websocat internal workings."
    )]
    no_lints: bool,

    #[structopt(
        short = "B",
        long = "buffer-size",
        help = "Maximum message size, in bytes",
        default_value = "65536"
    )]
    buffer_size: usize,

    #[structopt(
        short = "v", parse(from_occurrences), help = "Increase verbosity level to info or further"
    )]
    verbosity: u8,

    #[structopt(short = "q", help = "Suppress all diagnostic messages, except of startup errors")]
    quiet: bool,

    #[structopt(
        long = "queue-len",
        help = "[A] Number of pending queued messages for broadcast reuser",
        default_value = "16"
    )]
    broadcast_queue_len: usize,

    #[structopt(
        short = "S",
        long = "strict",
        help = "strict line/message mode: drop too long messages instead of splitting them, drop incomplete lines."
    )]
    strict_mode: bool,

    #[structopt(
        short = "0", long = "null-terminated", help = "Use \\0 instead of \\n for linemode"
    )]
    linemode_zero_terminated: bool,

    #[structopt(
        long = "restrict-uri",
        help = "When serving a websocket, only accept the given URI, like `/ws`\nThis liberates other URIs for things like serving static files or proxying."
    )]
    restrict_uri: Option<String>,

    #[structopt(
        short = "F",
        long = "static-file",
        help = "Serve a named static file for non-websocket connections.\nArgument syntax: <URI>:<Content-Type>:<file-path>\nArgument example: /index.html:text/html:index.html\nDirectories are not and will not be supported for security reasons.\nCan be specified multiple times.",
        parse(try_from_str = "interpret_static_file")
    )]
    serve_static_files: Vec<StaticFile>,
}

// TODO: make it byte-oriented/OsStr?
fn interpret_custom_header(x: &str) -> Result<(String, Vec<u8>)> {
    let colon = x.find(':');
    let colon = if let Some(colon) = colon {
        colon
    } else {
        Err("Argument to --header must contain `:` character")?
    };
    let hn = &x[0..colon];
    let mut hv = &x[colon + 1..];
    if hv.starts_with(' ') {
        hv = &x[colon + 2..];
    }
    Ok((hn.to_owned(), hv.as_bytes().to_vec()))
}

fn interpret_static_file(x: &str) -> Result<StaticFile> {
    let colon1 = match x.find(':') {
        Some(x) => x,
        None => Err("Argument to --static-file must contain two colons (`:`)")?
    };
    let uri = &x[0..colon1];
    let rest = &x[colon1+1..];
    let colon2 = match rest.find(':') {
        Some(x) => x,
        None => Err("Argument to --static-file must contain two colons (`:`)")?
    };
    let ct = &rest[0..colon2];
    let fp = &rest[colon2+1..];
    if uri.is_empty() || ct.is_empty() || fp.is_empty() {
        Err("Empty URI, content-type or path in --static-file parameter")?
    }
    Ok(StaticFile{
        uri: uri.to_string(),
        content_type: ct.to_string(),
        file: fp.into(),
    })
}

pub mod help;

// Based on https://github.com/rust-clique/clap-verbosity-flag/blob/master/src/lib.rs
mod logging {

    extern crate env_logger;
    extern crate log;

    use self::env_logger::Builder as LoggerBuilder;
    use self::log::Level;

    pub fn setup_env_logger(ll: u8) -> Result<(), Box<::std::error::Error>> {
        if ::std::env::var("RUST_LOG").is_ok() {
            if ll > 0 {
                eprintln!("websocat: RUST_LOG environment variable overrides any -v");
            }
            env_logger::init();
            return Ok(());
        }

        let lf = match ll {
            //0 => Level::Error,
            0 => Level::Warn,
            1 => Level::Info,
            2 => Level::Debug,
            _ => Level::Trace,
        }.to_level_filter();

        LoggerBuilder::new()
            .filter(Some("websocat"), lf)
            .filter(None, Level::Warn.to_level_filter())
            .try_init()?;
        Ok(())
    }

}

fn run() -> Result<()> {
    if std::env::args().nth(1).unwrap_or_default() == "--long-help" {
        help::longhelp();
        return Ok(());
    }
    if vec!["-?", "-h", "--help"].contains(&std::env::args().nth(1).unwrap_or_default().as_str()) {
        help::shorthelp();
        return Ok(());
    }

    let mut cmd = Opt::from_args();

    let mut quiet = cmd.quiet;

    if let Some(h) = cmd.help {
        if &h == "long" || &h == "full" {
            help::longhelp();
            return Ok(());
        } else if &h == "doc" {
            help::dochelp();
            return Ok(());
        }

        help::shorthelp();
        return Ok(());
    }

    let mut recommend_explicit_text_or_bin = false;

    if cmd.websocket_binary_mode && cmd.websocket_text_mode {
        Err("--binary and --text are mutually exclusive")?;
    }
    if !cmd.websocket_binary_mode && !cmd.websocket_text_mode {
        cmd.websocket_text_mode = true;
        recommend_explicit_text_or_bin = true;
    }

    //if !cmd.serve_static_files.is_empty() && cmd.restrict_uri.is_none() {
    //    Err("Specify --static-file is not supported without --restrict-uri")?
    //}

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

    #[cfg(feature = "openssl-probe")]
    {
        openssl_probe::init_ssl_cert_env_vars();
    }

    let mut opts: Options = Default::default();
    {
        macro_rules! opts {
            ($($o:ident)*) => {{
                $(opts.$o = cmd.$o;)*
            }};
        }
        opts!(
            websocket_text_mode
            websocket_protocol
            udp_oneshot_mode
            unidirectional
            unidirectional_reverse
            exit_on_eof
            oneshot
            unlink_unix_socket
            exec_args
            ws_c_uri
            linemode_strip_newlines
            origin
            custom_headers
            websocket_version
            websocket_dont_close
            one_message
            no_auto_linemode
            buffer_size
            linemode_zero_terminated
            restrict_uri
            serve_static_files
        )
    };

    let (s1, s2): (String, String) = match (cmd.addr1, cmd.addr2) {
        (None, None) => {
            help::shorthelp();
            return Err("No URL specified")?;
        }
        (Some(cmds1), Some(cmds2)) => {
            // Advanced mode
            if cmd.server_mode {
                Err("--server and two positional arguments are incompatible.\nBuild server command line without -s option, but with `listen` address types")?
            }
            (cmds1, cmds2)
        }
        (Some(cmds1), None) => {
            // Easy mode
            recommend_explicit_text_or_bin = false;
            if cmd.server_mode {
                if cmds1.contains(':') {
                    if !quiet {
                        eprintln!("Listening on ws://{}/", cmds1);
                    }
                    (format!("ws-l:{}", cmds1), "-".to_string())
                } else {
                    if !quiet {
                        eprintln!("Listening on ws://127.0.0.1:{}/", cmds1);
                    }
                    (format!("ws-l:127.0.0.1:{}", cmds1), "-".to_string())
                }
            } else {
                if !(cmds1.starts_with("ws://") || cmds1.starts_with("wss://")) {
                    if !quiet {
                        eprintln!("Specify ws:// or wss:// URI to connect to a websocket");
                    }
                    Err("Invalid command-line parameters")?;
                }
                ("-".to_string(), cmds1)
            }
        }
        (None, Some(_)) => unreachable!(),
    };

    if opts.websocket_text_mode {
        opts.read_debt_handling = websocat::readdebt::DebtHandling::Warn;
    }
    if cmd.strict_mode {
        opts.read_debt_handling = websocat::readdebt::DebtHandling::DropMessage;
        opts.linemode_strict = true;
    }

    let websocat1 = WebsocatConfiguration1 {
        opts,
        addr1: s1,
        addr2: s2,
    };
    let mut websocat2 = websocat1.parse1()?;

    if websocat2.inetd_mode() {
        quiet = true;
    }

    if !quiet && recommend_explicit_text_or_bin {
        eprintln!("websocat: It is recommended to either set --binary or --text explicitly");
    }
    if !quiet {
        logging::setup_env_logger(cmd.verbosity)?;
    }

    if !cmd.no_lints {
        websocat2.lint_and_fixup(&move |e: &str| {
            if !quiet {
                eprintln!("websocat: {}", e);
            }
        })?;
    }
    let websocat = websocat2.parse2()?;

    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(move |e| {
            if !quiet {
                eprintln!("websocat: {}", e);
            }
        }),
    );
    core.run(prog).map_err(|()| "error running".to_string())?;
    Ok(())
}

fn main() {
    let r = run();

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


<commit_before>extern crate clipboard;
extern crate byteorder;
extern crate argparse;

use clipboard::ClipboardContext;
use std::net::{TcpListener,TcpStream};
use std::io::{Error, ErrorKind, Result  };
use std::thread;
use byteorder::{ReadBytesExt, WriteBytesExt,  LittleEndian};
use std::sync::mpsc::{Sender, channel};
use std::time::Duration;
use argparse::{ArgumentParser, Store};
use std::io::prelude::*;
use std::io;

struct Config {
    port : u16,
    localip : String,
    outsideip : String
}

fn run_reading(stream : &mut TcpStream,content : Sender<String>) -> Result<()>{
    loop {
        println!("Waiting for a data");
        let length : u32 = try!(stream.read_u32::<LittleEndian>());
        if length == 0{
            // ignore empty
            continue;
        }
        println!("Got the data size: {}", length);
        // let mut data = vec![0u8;length as usize];
        let mut data = vec![0u8;0];
        println!("");
        try!(stream.take(length as u64).read_to_end(&mut data));
        for v in data.iter(){
            print!("{} ",v);
        }
        match String::from_utf8(data){
            Ok(s) => {
                println!("Received new content from the other side: {}", s);
                let _ = content.send(s);
            }
            Err(_) => {
                println!("Failed to convert received content to string.");
            }
        };
    }
}

fn run_sync(stream : &mut TcpStream) -> Result<()>{
    let mut ctx = match ClipboardContext::new(){
        Ok(c) => c,
        Err(_) => return Err(Error::new(ErrorKind::Other,"Failed to create clipboard context"))
    };
    // println!("{}", ctx.get_contents().unwrap());
    //
    // ctx.set_contents("the new content".to_owned()).unwrap();
    let mut current_content = match ctx.get_contents(){
        Ok(s) => s,
        Err(_) => "".to_owned()
    };
    let (wx, rx) = channel::<String>();
    // // create thread which will read the stream for changes from outside, and apply changes to this system
    //
    let mut reading = stream.try_clone().unwrap();
    thread::spawn(move||{run_reading(&mut reading,wx)});
    let mut writer = stream.try_clone().unwrap();

    println!("initial clipboard value is: {}",current_content );

    let mut counter = 0;

    loop{
        thread::sleep(Duration::new(1,0));
        if counter % 64 == 0{
            println!("");
        }
        counter = counter + 1;
        print!("-");
        io::stdout().flush().ok().expect("Could not flush stdout");
        match ctx.get_contents(){
            Ok(s) => {

                if s != current_content{
                    println!("A new content, sending it to the other side: {}!={}",s,current_content);
                    current_content = s.clone();
                    let data = s.as_bytes();
                    for v in data.iter(){
                        print!("{} ",v );
                    }
                    println!("");
                    try!(writer.write_u32::<LittleEndian>(data.len() as u32));
                    try!(writer.write(data));
                }
                match rx.try_recv(){
                    Ok(v) =>{
                        println!("Received a new value for cb: {}", v );
                        if v != current_content{
                            current_content = v.clone();
                            match ctx.set_contents(v){
                                Ok(_) => println!("Seting the new value as clipboard content."),
                                Err(_) => println!("Failde to set the new value as clipboard content.")
                            }
                        }
                    }
                    Err(_) => ()
                }
            }
            Err(_) => ()
        };

    }
}

fn try_run_client(config : &Config) -> Result<()>{
    let mut stream = try!(TcpStream::connect((&config.outsideip as &str,config.port)));
    run_sync(&mut stream)
}

fn run_server(config : &Config) -> Result<()> {
    let listener = try!(TcpListener::bind((&config.localip as &str,config.port)));

    for stream in listener.incoming(){
        match stream{
            Ok(stream) => {
                thread::spawn(move||{
                    println!("connected");
                    run_sync(&mut stream.try_clone().unwrap())
                });
            }
            Err(e) => {
                println!("Connection failed {}",e );
            }
        }
    }

    drop(listener);
    Ok(())
}

fn main() {
    let mut config = Config{
        port: 24011,
        localip : "127.0.0.1".to_owned(),
        outsideip : "127.0.0.1".to_owned()
    };
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Synchronise clipboard content between two computers.");
        ap.refer(&mut config.port)
            .add_option(&["-p","--port"],Store,"Port address");
        ap.refer(&mut config.localip)
            .add_option(&["-l","--local"],Store,"Local ip address");
        ap.refer(&mut config.outsideip)
            .add_option(&["-o","--outside"],Store,"Outside ip address");
        ap.parse_args_or_exit();
    }
    println!("local: {}, outside: {}, port: {}",config.localip, config.outsideip, config.port );
    let res = match try_run_client(&config){
        Err(_) => {
            println!("Could not connect to server, creating own.");
            run_server(&config)},
        _ => Ok(())
    };

    match res {
        Ok(_) => println!("done"),
        Err(e) => println!("Failed: {}", e),
    }
}
<commit_msg>Added option to skip connecting to server at the start.<commit_after>extern crate clipboard;
extern crate byteorder;
extern crate argparse;

use clipboard::ClipboardContext;
use std::net::{TcpListener,TcpStream};
use std::io::{Error, ErrorKind, Result  };
use std::thread;
use byteorder::{ReadBytesExt, WriteBytesExt,  LittleEndian};
use std::sync::mpsc::{Sender, channel};
use std::time::Duration;
use argparse::{ArgumentParser, Store,StoreTrue};
use std::io::prelude::*;
use std::io;

struct Config {
    skip_client : bool,
    port : u16,
    localip : String,
    outsideip : String
}

fn run_reading(stream : &mut TcpStream,content : Sender<String>) -> Result<()>{
    loop {
        println!("Waiting for a data");
        let length : u32 = try!(stream.read_u32::<LittleEndian>());
        if length == 0{
            // ignore empty
            continue;
        }
        println!("Got the data size: {}", length);
        // let mut data = vec![0u8;length as usize];
        let mut data = vec![0u8;0];
        println!("");
        try!(stream.take(length as u64).read_to_end(&mut data));
        for v in data.iter(){
            print!("{} ",v);
        }
        match String::from_utf8(data){
            Ok(s) => {
                println!("Received new content from the other side: {}", s);
                let _ = content.send(s);
            }
            Err(_) => {
                println!("Failed to convert received content to string.");
            }
        };
    }
}

fn run_sync(stream : &mut TcpStream) -> Result<()>{
    let mut ctx = match ClipboardContext::new(){
        Ok(c) => c,
        Err(_) => return Err(Error::new(ErrorKind::Other,"Failed to create clipboard context"))
    };
    // println!("{}", ctx.get_contents().unwrap());
    //
    // ctx.set_contents("the new content".to_owned()).unwrap();
    let mut current_content = match ctx.get_contents(){
        Ok(s) => s,
        Err(_) => "".to_owned()
    };
    let (wx, rx) = channel::<String>();
    // // create thread which will read the stream for changes from outside, and apply changes to this system
    //
    let mut reading = stream.try_clone().unwrap();
    thread::spawn(move||{run_reading(&mut reading,wx)});
    let mut writer = stream.try_clone().unwrap();

    println!("initial clipboard value is: {}",current_content );

    let mut counter = 0;

    loop{
        thread::sleep(Duration::new(1,0));
        if counter % 64 == 0{
            println!("");
        }
        counter = counter + 1;
        print!("-");
        io::stdout().flush().ok().expect("Could not flush stdout");
        match ctx.get_contents(){
            Ok(s) => {

                if s != current_content{
                    println!("A new content, sending it to the other side: {}!={}",s,current_content);
                    current_content = s.clone();
                    let data = s.as_bytes();
                    for v in data.iter(){
                        print!("{} ",v );
                    }
                    println!("");
                    try!(writer.write_u32::<LittleEndian>(data.len() as u32));
                    try!(writer.write(data));
                }
                match rx.try_recv(){
                    Ok(v) =>{
                        println!("Received a new value for cb: {}", v );
                        if v != current_content{
                            current_content = v.clone();
                            match ctx.set_contents(v){
                                Ok(_) => println!("Seting the new value as clipboard content."),
                                Err(_) => println!("Failde to set the new value as clipboard content.")
                            }
                        }
                    }
                    Err(_) => ()
                }
            }
            Err(_) => ()
        };

    }
}

fn try_run_client(config : &Config) -> Result<()>{
    println!("Trying to connect to {}:{}",config.outsideip, config.port );
    let mut stream = try!(TcpStream::connect((&config.outsideip as &str,config.port)));
    run_sync(&mut stream)
}

fn run_server(config : &Config) -> Result<()> {
    println!("Creating server on {}:{}" , config.localip,config.port );
    let listener = try!(TcpListener::bind((&config.localip as &str,config.port)));
    println!("Waiting for new connections");

    for stream in listener.incoming(){
        match stream{
            Ok(stream) => {
                thread::spawn(move||{
                    println!("connected");
                    run_sync(&mut stream.try_clone().unwrap())
                });
            }
            Err(e) => {
                println!("Connection failed {}",e );
            }
        }
    }

    drop(listener);
    Ok(())
}

fn main() {
    let mut config = Config{
        skip_client : false,
        port: 24011,
        localip : "127.0.0.1".to_owned(),
        outsideip : "127.0.0.1".to_owned()
    };
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Synchronise clipboard content between two computers.");
        ap.refer(&mut config.skip_client)
            .add_option(&["-s","--skip_client"],StoreTrue,"Skip connecting to client and create server right away.");
        ap.refer(&mut config.port)
            .add_option(&["-p","--port"],Store,"Port address");
        ap.refer(&mut config.localip)
            .add_option(&["-l","--local"],Store,"Local ip address");
        ap.refer(&mut config.outsideip)
            .add_option(&["-o","--outside"],Store,"Outside ip address");
        ap.parse_args_or_exit();
    }
    println!("local: {}, outside: {}, port: {}",config.localip, config.outsideip, config.port );
    let _ = if config.skip_client{
        println!("Skiping connecting to client");
        run_server(&config)
    }else {match try_run_client(&config){
        Err(_) => {
            println!("Could not connect to server, creating own.");
            run_server(&config)},
        _ => Ok(())
    }};
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate serde_json;
extern crate wdsapi;
extern crate rayon;

use clap::{App, AppSettings, Arg, SubCommand};
use rayon::prelude::*;
use serde_json::de::from_reader;
use serde_json::ser::to_string_pretty;
use std::thread::{JoinHandle, spawn};
use wdsapi::{Collection, Configuration, Credentials, Environment,
             NewCollection, NewEnvironment, Status, create_collection,
             create_configuration, create_environment, credentials_from_file,
             delete_environment, get_collection_detail, get_collections,
             get_configurations, get_environments};

#[derive(Clone, Debug)]
struct EnvironmentInfo {
    environment: Environment,
    configurations: Vec<Configuration>,
    collections: Vec<Collection>,
}

#[derive(Clone, Debug)]
struct DiscoveryServiceInfo {
    creds: Credentials,
    environments: Vec<EnvironmentInfo>,
}

fn get_configurations_thread(creds: &Credentials,
                             env_id: &str)
                             -> JoinHandle<Vec<Configuration>> {
    let creds = creds.clone();
    let env_id = env_id.to_string();
    spawn(move || {
        get_configurations(&creds, &env_id)
            .expect("Failed to get configuration information, in thread")
            .configurations
    })
}

fn get_collections_thread(creds: &Credentials,
                          env_id: &str)
                          -> JoinHandle<Vec<Collection>> {
    let creds = creds.clone();
    let env_id = env_id.to_string();
    spawn(move || {
        get_collections(&creds, &env_id)
            .expect("Failed to get collection information, in thread")
            .collections
            .par_iter()
            .map({
                |col| {
                    get_collection_detail(&creds, &env_id, &col.collection_id)
                        .expect("Failed to get collection detail, in thread")
                }
            })
            .weight_max()
            .collect()
    })
}

fn environment_info(creds: &Credentials, env: &Environment) -> EnvironmentInfo {
    let env_id = env.environment_id.clone();
    // launch threads for these API calls
    let conf_thread = get_configurations_thread(creds, &env_id);
    let col_thread = get_collections_thread(creds, &env_id);
    // Gather the results from the threads
    let configurations = conf_thread.join()
                                    .expect("Failed to get configuration \
                                             information");
    let collections = col_thread.join()
                                .expect("Failed to get collection information");
    EnvironmentInfo {
        environment: env.clone(),
        configurations: configurations,
        collections: collections,
    }
}

fn discovery_service_info(matches: &clap::ArgMatches) -> DiscoveryServiceInfo {
    let creds_file = matches.value_of("credentials")
                            .expect("Internal error: Missing credentials?");
    let creds = credentials_from_file(creds_file).expect("Invalid credentials");
    let environments = get_environments(&creds)
        .expect("Failed to get environment information")
        .environments
        .par_iter()
        .map({
            |env| environment_info(&creds, env)
        })
        .weight_max()
        .collect();
    DiscoveryServiceInfo {
        creds: creds,
        environments: environments,
    }
}

fn writable_environment(info: &DiscoveryServiceInfo) -> EnvironmentInfo {
    let writables: Vec<EnvironmentInfo> =
        info.environments
            .clone()
            .into_iter()
            .filter({
                |env| !env.environment.read_only
            })
            .collect();

    assert!(writables.len() <= 1,
            format!("Multiple writable environments found {:?}", writables));

    writables.first()
             .expect("No writable environment found")
             .clone()
}

fn newest_configuration(env: &EnvironmentInfo) -> Configuration {
    let mut newest = env.configurations
                        .clone()
                        .into_iter()
                        .last()
                        .expect("No configuration found");
    for conf in env.configurations.clone() {
        if conf.created > newest.created {
            newest = conf
        }
    }
    newest
}

fn newest_collection(env: &EnvironmentInfo) -> Collection {
    let mut newest = env.collections
                        .clone()
                        .into_iter()
                        .last()
                        .expect("No collection found");
    for col in env.collections.clone() {
        if col.created > newest.created {
            newest = col
        }
    }
    newest
}

fn configuration(env: &EnvironmentInfo,
                 configuration_id: &str)
                 -> Configuration {
    env.configurations
       .clone()
       .into_iter()
       .filter({
           |c| c.configuration_id == Some(configuration_id.to_string())
       })
       .last()
       .expect("No configuration found")
       .clone()
}

fn print_env_children(env: &EnvironmentInfo) {
    let mut first = true;
    let configs: &Vec<Configuration> = &env.configurations;
    let collections: &Vec<Collection> = &env.collections;
    for conf in configs {
        if first {
            first = false;
            println!("   Configurations: {}", conf.name)
        } else {
            println!("                   {}", conf.name)
        }
    }
    first = true;
    for col in collections {
        let counts = col.document_counts.clone().unwrap();
        let formatted_counts = if counts.failed > 0 {
            format!("{} available, {} processing, {} failed",
                    counts.available,
                    counts.processing,
                    counts.failed)
        } else if counts.processing > 0 {
            format!("{} available, {} processing",
                    counts.available,
                    counts.processing)
        } else {
            format!("{} available", counts.available)
        };

        if first {
            first = false;
            println!("   Collections: {}, {}", col.name, formatted_counts)
        } else {
            println!("                {}, {}", col.name, formatted_counts)
        }
    }
}

fn show(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    for env in info.environments {
        let status = match env.environment.status {
            Status::Pending => " - pending",
            _ => {
                if env.environment.read_only {
                    " - read only"
                } else {
                    ""
                }
            }
        };
        let capacity = env.environment.index_capacity.as_ref();
        match capacity {
            Some(index_capacity) => {
                println!("Environment: {}, {} disk, {} memory{}",
                         env.environment.name,
                         index_capacity.disk_usage.total,
                         index_capacity.memory_usage.total,
                         status)
            }
            None => {
                println!("Environment: {}{}", env.environment.name, status);
            }
        }
        print_env_children(&env)
    }
}

// I suppose there is a standard library way to do this...
fn optional_string(s: &Option<&str>) -> Option<String> {
    match *s {
        Some(s) => Some(s.to_string()),
        None => None,
    }
}

fn cenv(matches: &clap::ArgMatches) {
    let creds_file = matches.value_of("credentials").unwrap();
    let creds = credentials_from_file(creds_file).unwrap(); // FIXME

    let env_options = NewEnvironment {
        name: matches.value_of("name").unwrap().to_string(),
        description: optional_string(&matches.value_of("description")),
        size: matches.value_of("size").unwrap_or("0").parse::<u64>().unwrap(),
    };
    match create_environment(&creds, &env_options) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_environment response"))
        }
        Err(e) => println!("Failed to create environment {}", e),
    }
}

fn denv(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env_id = writable_environment(&info).environment.environment_id;

    match delete_environment(&info.creds, &env_id) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  delete_environment response"))
        }
        Err(e) => println!("Failed to delete environment {}", e),
    }
}

fn ccol(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);
    let env_id = env.environment.environment_id.clone();

    let col_options = NewCollection {
        name: matches.value_of("name").unwrap().to_string(),
        description: optional_string(&matches.value_of("description")),
        configuration_id: match matches.value_of("configuration_id") {
            Some(s) => Some(s.to_string()),
            None => newest_configuration(&env).configuration_id,
        },
    };

    match create_collection(&info.creds, &env_id, &col_options) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_collection response"))
        }
        Err(e) => println!("Failed to create collection {}", e),
    }
}

fn cconfig(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);
    let env_id = env.environment.environment_id.clone();
    let config_filename =
        matches.value_of("configuration").unwrap().to_string();
    let config_file = std::fs::File::open(config_filename)
        .expect("Failed to read configuration JSON file");
    let config: Configuration = from_reader(config_file)
        .expect("Failed to parse configuration JSON");

    match create_configuration(&info.creds, &env_id, &config) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_collection response"))
        }
        Err(e) => println!("Failed to create collection {}", e),
    }
}

fn crawler(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);

    let collection = newest_collection(&env);
    let config = configuration(&env, &collection.configuration_id);

    println!("# discovery_service.conf \
              generated by https://github.com/bruceadams/wdsapi
{{
    environment_id   = {:?} # {}
    collection_id    = {:?} # {}
    configuration_id = {:?} # {}

    base_url = {:?}
    credentials {{
            username = {:?}
            password = {:?}
    }}

    api_version = \"2016-11-07\"
    concurrent_upload_connection_limit = 2
    http_timeout = 125
    send_stats {{
            jvm = true
            os  = true
    }}
}}
",
             env.environment.environment_id,
             env.environment.name,
             collection.collection_id,
             collection.name,
             collection.configuration_id,
             config.name,
             info.creds.url,
             info.creds.username,
             info.creds.password);
}

fn main() {
    rayon::initialize(rayon::Configuration::new().set_num_threads(64))
        .expect("Failed to initialize thread pool");

    let matches = App::new("wdscli")
        .about("Basic administration for Watson Discovery Service.")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("show")
            .about("Displays information about existing resources.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
            // .arg(Arg::with_name("guid")
            //     .short("g")
            //     .help("Displays the GUID for each resource.")))
        .subcommand(SubCommand::with_name("create-environment")
            .visible_alias("ce")
            .about("Create a writable environment")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("name")
                .required(true)
                .help("The name of the environment."))
            .arg(Arg::with_name("size")
                .short("s")
                .takes_value(true)
                .help("The size environment to create."))
            .arg(Arg::with_name("description")
                .short("d")
                .takes_value(true)
                .help("Description text for the environment.")))
        .subcommand(SubCommand::with_name("delete-environment")
            .visible_alias("de")
            .about("Delete the writable environment")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
        .subcommand(SubCommand::with_name("create-collection")
            .visible_alias("cl")
            .about("Create a new collection using the most recently created \
                    configuration")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("name")
                .required(true)
                .help("The name of the collection."))
            .arg(Arg::with_name("description")
                .short("d")
                .short("desc")
                .takes_value(true)
                .help("Description text for the collection.")))
        .subcommand(SubCommand::with_name("crawler-configuration")
            .visible_alias("cc")
            .about("Print out crawler configuration.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
        .subcommand(SubCommand::with_name("create-configuration")
            .visible_alias("cn")
            .about("Create a new configuration, or replace existing \
                    configuration with the same name.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("configuration")
                .required(true)
                .help("File containing the configuration JSON.")))
        .subcommand(SubCommand::with_name("query")
            .visible_alias("q")
            .about("Issue a query.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("query").help("The query string."))
            .arg(Arg::with_name("count")
                .short("n")
                .takes_value(true)
                .help("The number of results to return. Defaults to 2."))
            .arg(Arg::with_name("collection")
                .short("c")
                .takes_value(true)
                .help("The name of the collection to query. \
                       Defaults to every collection.")))
        .get_matches();

    match matches.subcommand() {
        ("show", Some(m)) => show(m),
        ("create-environment", Some(m)) => cenv(m),
        ("delete-environment", Some(m)) => denv(m),
        ("create-collection", Some(m)) => ccol(m),
        ("create-configuration", Some(m)) => cconfig(m),
        ("crawler-configuration", Some(m)) => crawler(m),
        _ => println!("Not implemented yet; sorry!"),
    }
}
<commit_msg>Simplify finding newest<commit_after>extern crate clap;
extern crate serde_json;
extern crate wdsapi;
extern crate rayon;

use clap::{App, AppSettings, Arg, SubCommand};
use rayon::prelude::*;
use serde_json::de::from_reader;
use serde_json::ser::to_string_pretty;
use std::thread::{JoinHandle, spawn};
use wdsapi::{Collection, Configuration, Credentials, Environment,
             NewCollection, NewEnvironment, Status, create_collection,
             create_configuration, create_environment, credentials_from_file,
             delete_environment, get_collection_detail, get_collections,
             get_configurations, get_environments};

#[derive(Clone, Debug)]
struct EnvironmentInfo {
    environment: Environment,
    configurations: Vec<Configuration>,
    collections: Vec<Collection>,
}

#[derive(Clone, Debug)]
struct DiscoveryServiceInfo {
    creds: Credentials,
    environments: Vec<EnvironmentInfo>,
}

fn get_configurations_thread(creds: &Credentials,
                             env_id: &str)
                             -> JoinHandle<Vec<Configuration>> {
    let creds = creds.clone();
    let env_id = env_id.to_string();
    spawn(move || {
        get_configurations(&creds, &env_id)
            .expect("Failed to get configuration information, in thread")
            .configurations
    })
}

fn get_collections_thread(creds: &Credentials,
                          env_id: &str)
                          -> JoinHandle<Vec<Collection>> {
    let creds = creds.clone();
    let env_id = env_id.to_string();
    spawn(move || {
        get_collections(&creds, &env_id)
            .expect("Failed to get collection information, in thread")
            .collections
            .par_iter()
            .map({
                |col| {
                    get_collection_detail(&creds, &env_id, &col.collection_id)
                        .expect("Failed to get collection detail, in thread")
                }
            })
            .weight_max()
            .collect()
    })
}

fn environment_info(creds: &Credentials, env: &Environment) -> EnvironmentInfo {
    let env_id = env.environment_id.clone();
    // launch threads for these API calls
    let conf_thread = get_configurations_thread(creds, &env_id);
    let col_thread = get_collections_thread(creds, &env_id);
    // Gather the results from the threads
    let configurations = conf_thread.join()
                                    .expect("Failed to get configuration \
                                             information");
    let collections = col_thread.join()
                                .expect("Failed to get collection information");
    EnvironmentInfo {
        environment: env.clone(),
        configurations: configurations,
        collections: collections,
    }
}

fn discovery_service_info(matches: &clap::ArgMatches) -> DiscoveryServiceInfo {
    let creds_file = matches.value_of("credentials")
                            .expect("Internal error: Missing credentials?");
    let creds = credentials_from_file(creds_file).expect("Invalid credentials");
    let environments = get_environments(&creds)
        .expect("Failed to get environment information")
        .environments
        .par_iter()
        .map({
            |env| environment_info(&creds, env)
        })
        .weight_max()
        .collect();
    DiscoveryServiceInfo {
        creds: creds,
        environments: environments,
    }
}

fn writable_environment(info: &DiscoveryServiceInfo) -> EnvironmentInfo {
    let writables: Vec<EnvironmentInfo> =
        info.environments
            .clone()
            .into_iter()
            .filter({
                |env| !env.environment.read_only
            })
            .collect();

    assert!(writables.len() <= 1,
            format!("Multiple writable environments found {:?}", writables));

    writables.first()
             .expect("No writable environment found")
             .clone()
}

fn newest_configuration(env: &EnvironmentInfo) -> Configuration {
    env.configurations
       .clone()
       .into_iter()
       .max_by_key({
           |i| i.created
       })
       .expect("No configurations found")
}

fn newest_collection(env: &EnvironmentInfo) -> Collection {
    env.collections
       .clone()
       .into_iter()
       .max_by_key({
           |i| i.created
       })
       .expect("No collections found")
}

fn configuration(env: &EnvironmentInfo,
                 configuration_id: &str)
                 -> Configuration {
    env.configurations
       .clone()
       .into_iter()
       .filter({
           |c| c.configuration_id == Some(configuration_id.to_string())
       })
       .last()
       .expect("No configuration found")
       .clone()
}

fn print_env_children(env: &EnvironmentInfo) {
    let mut first = true;
    let configs: &Vec<Configuration> = &env.configurations;
    let collections: &Vec<Collection> = &env.collections;
    for conf in configs {
        if first {
            first = false;
            println!("   Configurations: {}", conf.name)
        } else {
            println!("                   {}", conf.name)
        }
    }
    first = true;
    for col in collections {
        let counts = col.document_counts.clone().unwrap();
        let formatted_counts = if counts.failed > 0 {
            format!("{} available, {} processing, {} failed",
                    counts.available,
                    counts.processing,
                    counts.failed)
        } else if counts.processing > 0 {
            format!("{} available, {} processing",
                    counts.available,
                    counts.processing)
        } else {
            format!("{} available", counts.available)
        };

        if first {
            first = false;
            println!("   Collections: {}, {}", col.name, formatted_counts)
        } else {
            println!("                {}, {}", col.name, formatted_counts)
        }
    }
}

fn show(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    for env in info.environments {
        let status = match env.environment.status {
            Status::Pending => " - pending",
            _ => {
                if env.environment.read_only {
                    " - read only"
                } else {
                    ""
                }
            }
        };
        let capacity = env.environment.index_capacity.as_ref();
        match capacity {
            Some(index_capacity) => {
                println!("Environment: {}, {} disk, {} memory{}",
                         env.environment.name,
                         index_capacity.disk_usage.total,
                         index_capacity.memory_usage.total,
                         status)
            }
            None => {
                println!("Environment: {}{}", env.environment.name, status);
            }
        }
        print_env_children(&env)
    }
}

// I suppose there is a standard library way to do this...
fn optional_string(s: &Option<&str>) -> Option<String> {
    match *s {
        Some(s) => Some(s.to_string()),
        None => None,
    }
}

fn cenv(matches: &clap::ArgMatches) {
    let creds_file = matches.value_of("credentials").unwrap();
    let creds = credentials_from_file(creds_file).unwrap(); // FIXME

    let env_options = NewEnvironment {
        name: matches.value_of("name").unwrap().to_string(),
        description: optional_string(&matches.value_of("description")),
        size: matches.value_of("size").unwrap_or("0").parse::<u64>().unwrap(),
    };
    match create_environment(&creds, &env_options) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_environment response"))
        }
        Err(e) => println!("Failed to create environment {}", e),
    }
}

fn denv(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env_id = writable_environment(&info).environment.environment_id;

    match delete_environment(&info.creds, &env_id) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  delete_environment response"))
        }
        Err(e) => println!("Failed to delete environment {}", e),
    }
}

fn ccol(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);
    let env_id = env.environment.environment_id.clone();

    let col_options = NewCollection {
        name: matches.value_of("name").unwrap().to_string(),
        description: optional_string(&matches.value_of("description")),
        configuration_id: match matches.value_of("configuration_id") {
            Some(s) => Some(s.to_string()),
            None => newest_configuration(&env).configuration_id,
        },
    };

    match create_collection(&info.creds, &env_id, &col_options) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_collection response"))
        }
        Err(e) => println!("Failed to create collection {}", e),
    }
}

fn cconfig(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);
    let env_id = env.environment.environment_id.clone();
    let config_filename =
        matches.value_of("configuration").unwrap().to_string();
    let config_file = std::fs::File::open(config_filename)
        .expect("Failed to read configuration JSON file");
    let config: Configuration = from_reader(config_file)
        .expect("Failed to parse configuration JSON");

    match create_configuration(&info.creds, &env_id, &config) {
        Ok(response) => {
            println!("{}",
                     to_string_pretty(&response)
                         .expect("Internal error: failed to format \
                                  create_collection response"))
        }
        Err(e) => println!("Failed to create collection {}", e),
    }
}

fn crawler(matches: &clap::ArgMatches) {
    let info = discovery_service_info(matches);
    let env = writable_environment(&info);

    let collection = newest_collection(&env);
    let config = configuration(&env, &collection.configuration_id);

    println!("# discovery_service.conf \
              generated by https://github.com/bruceadams/wdsapi
{{
    environment_id   = {:?} # {}
    collection_id    = {:?} # {}
    configuration_id = {:?} # {}

    base_url = {:?}
    credentials {{
            username = {:?}
            password = {:?}
    }}

    api_version = \"2016-11-07\"
    concurrent_upload_connection_limit = 2
    http_timeout = 125
    send_stats {{
            jvm = true
            os  = true
    }}
}}
",
             env.environment.environment_id,
             env.environment.name,
             collection.collection_id,
             collection.name,
             collection.configuration_id,
             config.name,
             info.creds.url,
             info.creds.username,
             info.creds.password);
}

fn main() {
    rayon::initialize(rayon::Configuration::new().set_num_threads(64))
        .expect("Failed to initialize thread pool");

    let matches = App::new("wdscli")
        .about("Basic administration for Watson Discovery Service.")
        .setting(AppSettings::SubcommandRequired)
        .subcommand(SubCommand::with_name("show")
            .about("Displays information about existing resources.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
            // .arg(Arg::with_name("guid")
            //     .short("g")
            //     .help("Displays the GUID for each resource.")))
        .subcommand(SubCommand::with_name("create-environment")
            .visible_alias("ce")
            .about("Create a writable environment")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("name")
                .required(true)
                .help("The name of the environment."))
            .arg(Arg::with_name("size")
                .short("s")
                .takes_value(true)
                .help("The size environment to create."))
            .arg(Arg::with_name("description")
                .short("d")
                .takes_value(true)
                .help("Description text for the environment.")))
        .subcommand(SubCommand::with_name("delete-environment")
            .visible_alias("de")
            .about("Delete the writable environment")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
        .subcommand(SubCommand::with_name("create-collection")
            .visible_alias("cl")
            .about("Create a new collection using the most recently created \
                    configuration")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("name")
                .required(true)
                .help("The name of the collection."))
            .arg(Arg::with_name("description")
                .short("d")
                .short("desc")
                .takes_value(true)
                .help("Description text for the collection.")))
        .subcommand(SubCommand::with_name("crawler-configuration")
            .visible_alias("cc")
            .about("Print out crawler configuration.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials.")))
        .subcommand(SubCommand::with_name("create-configuration")
            .visible_alias("cn")
            .about("Create a new configuration, or replace existing \
                    configuration with the same name.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("configuration")
                .required(true)
                .help("File containing the configuration JSON.")))
        .subcommand(SubCommand::with_name("query")
            .visible_alias("q")
            .about("Issue a query.")
            .arg(Arg::with_name("credentials")
                .required(true)
                .help("A JSON file containing service credentials."))
            .arg(Arg::with_name("query").help("The query string."))
            .arg(Arg::with_name("count")
                .short("n")
                .takes_value(true)
                .help("The number of results to return. Defaults to 2."))
            .arg(Arg::with_name("collection")
                .short("c")
                .takes_value(true)
                .help("The name of the collection to query. \
                       Defaults to every collection.")))
        .get_matches();

    match matches.subcommand() {
        ("show", Some(m)) => show(m),
        ("create-environment", Some(m)) => cenv(m),
        ("delete-environment", Some(m)) => denv(m),
        ("create-collection", Some(m)) => ccol(m),
        ("create-configuration", Some(m)) => cconfig(m),
        ("crawler-configuration", Some(m)) => crawler(m),
        _ => println!("Not implemented yet; sorry!"),
    }
}
<|endoftext|>


<commit_before>extern crate rayon;

use rayon::prelude::*;

use std::process::Command;
use std::fs::File;
use std::io::Write;
use std::os::raw;
use std::io::Read;

pub mod descriptions;

use descriptions::*;

#[cfg(target_os="windows")]
const VASM_EXE: &'static str = "bin/win/vasmm68k_mot.exe";

#[cfg(target_os="macos")]
const VASM_EXE: &'static str = "bin/mac/vasmm68k_mot";

#[cfg(any(target_os="linux",
          target_os="freebsd",
          target_os="dragonfly",
          target_os="netbsd",
          target_os="openbsd"))]
const VASM_EXE: &'static str = "vasmm68k_mot";

struct BuildResult {
    src: Option<Op>,
    dst: Op,
    temp_file: String,
    temp_out: String,
    cycle_count: Option<usize>,
}

#[derive(Copy, Clone)]
struct Op {
    name: &'static str,
    print_name: &'static str,
}

impl Op {
    fn new(name: &'static str, print_name: &'static str) -> Op {
        Op {
            name: name,
            print_name: print_name,
        }
    }
}
struct Instruction {
    name: &'static str,
    _desc: Option<Description>,
}

fn compile_statement(filename: &str, file_out: &str, statement: &str) -> bool {
    {
        let mut file = File::create(filename).unwrap();
        write!(file, " {}", statement).unwrap();
    }

    let output = Command::new(VASM_EXE)
        .arg("-no-opt")
        .arg("-m68000")
        .arg(filename)
        .arg("-Fbin")
        .arg("-o")
        .arg(file_out)
        .output()
        .expect("failed to execute process");

    output.status.success()
}

fn print_grid_table(name: &str, cycles: &Vec<BuildResult>, src_table: &[Op], dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    for src in src_table {
        let mut skip_count = 0;

        for i in index..index + 9 {
            if let None = cycles[i].cycle_count {
                skip_count += 1;
            }
        }

        if skip_count != 9 {
            print!("| {name:<width$}", name = src.print_name, width = 9);

            for dest in dest_table {
                if let Some(cycle_count) = cycles[index].cycle_count {
                    print!("|{number:^width$}",
                           number = cycle_count,
                           width = dest.print_name.len() + 2);
                } else {
                    print!("|{number:^width$}",
                           number = "*",
                           width = dest.print_name.len() + 2);
                }
                index += 1;
            }

            println!("|");
        } else {
            index += 9;
        }
    }

    println!("");
}

fn print_table(name: &str, cycles: &Vec<BuildResult>, dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    print!("| {name:<width$}", name = " ", width = 9);

    for dest in dest_table {
        if let Some(cycle_count) = cycles[index].cycle_count {
            print!("|{number:^width$}",
                   number = cycle_count,
                   width = dest.print_name.len() + 2);
        } else {
            print!("|{number:^width$}",
                   number = "*",
                   width = dest.print_name.len() + 2);
        }
        index += 1;
    }

    println!("|");
    println!("");
}

fn print_instruction_header(inst: &Instruction) {
    let name = inst.name.to_uppercase();
    println!("## {}\n", name);
    if let Some(ref desc) = inst._desc {
        println!("**Operation:**      {}\n", desc.operation);

        print!("**Assembler:** ");
        for assem in desc.assembler {
            print!("{}        ", assem);
        }
        println!("\n\n**Attributes:** Size = ({})\n", desc.attributes);

        println!("**Description:** {}\n", desc.description);
    }
    else {
        println!("__No Description__\n");
    }
}

fn compile_cycle_counts(statements: &mut Vec<BuildResult>) {
    let mut instructions = Vec::new();
    let mut inst_count = 0u32;
    let mut cycle_count = Vec::<u32>::new();

    for statement in statements.iter() {
        if statement.cycle_count.is_some() {
            {
                let mut f = File::open(&statement.temp_out).unwrap();
                f.read_to_end(&mut instructions).unwrap();
                cycle_count.push(0);
                inst_count += 1;
            }
        }
    }

    unsafe {
        m68k_run_instructions(instructions.as_ptr() as *const raw::c_void, inst_count, cycle_count.as_mut_ptr());
    }

    inst_count = 0;

    for statement in statements.iter_mut() {
        if statement.cycle_count.is_some() {
            statement.cycle_count = Some(cycle_count[inst_count as usize] as usize);
            inst_count += 1;
        }
    }
}

fn generate_table(name: &str,
                  is_long: bool,
                  src_table: Option<&[Op]>,
                  dest_table: &[Op]) {
    let mut statements = Vec::with_capacity(20 * 20);
    let mut count = 0;

    if let Some(src_opts) = src_table {
        for src in src_opts {
            for dst in dest_table {
                let file_in = format!("target/temp_{}.s", count);
                let file_out = format!("target/temp_{}.o", count);

                statements.push(BuildResult {
                    src: Some(src.clone()),
                    dst: dst.clone(),
                    temp_file: file_in,
                    temp_out: file_out,
                    cycle_count: None,
                });

                count += 1;
            }
        }

        statements.par_iter_mut().weight_max().for_each(|v| {
            let src = v.src.unwrap();
            let statement;

            if src.print_name == "#xxx" && is_long {
                statement = format!("{} {},{}", name, "#$ffffff", v.dst.name);
            } else {
                statement = format!("{} {},{}", name, v.src.unwrap().name, v.dst.name);
            }

            //println!("Statement {}", statement);
            if compile_statement(&v.temp_file, &v.temp_out, &statement) {
                v.cycle_count = Some(0); // indicate that this should be processed
            }
        });

        compile_cycle_counts(&mut statements);

        print_grid_table(name, &statements, &src_opts, dest_table);
    } else {
        for dst in dest_table {
            let file_in = format!("target/temp_{}.s", count);
            let file_out = format!("target/temp_{}.o", count);

            statements.push(BuildResult {
                src: None,
                dst: dst.clone(),
                temp_file: file_in,
                temp_out: file_out,
                cycle_count: None,
            });

            count += 1;
        }

        statements.par_iter_mut().weight_max().for_each(|v| {
            let statement = format!("{} {}", name, v.dst.name);
            if compile_statement(&v.temp_file, &v.temp_out, &statement) {
                v.cycle_count = Some(0);
            }
        });

        compile_cycle_counts(&mut statements);

        print_table(name, &statements, dest_table);
    }
}

fn main() {
    let dest_types = [
        Op::new("d0", "Dn"),
        Op::new("a0", "An"),
        Op::new("(a0)", "(An)"),
        Op::new("(a0)+", "(An)+"),
        Op::new("-(a0)", "-(An)"),
        Op::new("2(a0)", "d(An)"),
        Op::new("2(a0,d0)", "d(An,Dn)"),
        Op::new("$4.W", "xxx.W"),
        Op::new("$4.L", "xxx.L")];

    let src_types = [
        Op::new("d0", "Dn"),
        Op::new("a0", "An"),
        Op::new("(a0)", "(An)"),
        Op::new("(a0)+", "(An)+"),
        Op::new("-(a0)", "-(An)"),
        Op::new("2(a0)", "d(An)"),
        Op::new("2(a0,d0)", "d(An,Dn)"),
        Op::new("$4.W", "xxx.W"),
        Op::new("$4.L", "xxx.L"),
        Op::new("2(pc)", "d(PC)"),
        Op::new("2(pc,d0)", "d(PC,Dn)"),
        Op::new("#2000", "#xxx")];

    let dest_types_none = [Op::new("", "")];

    unsafe {
        m68k_wrapper_init();
    }

    let inst_2_ops_000 =
        [
        Instruction {
            name: "abcd",
            _desc: Some(ABCD_DESC),
        },

        Instruction {
            name: "add",
            _desc: Some(ADD_DESC),
        },
        Instruction {
            name: "addq",
            _desc: Some(ADDQ_DESC),
        },
        Instruction {
            name: "addx",
            _desc: Some(ADDX_DESC),
        },
        Instruction {
            name: "and",
            _desc: Some(AND_DESC),
        },
        Instruction {
            name: "bchg",
            _desc: Some(BCHG_DESC),
        },
        Instruction {
            name: "bclr",
            _desc: Some(BCLR_DESC),
        },
        Instruction {
            name: "bset",
            _desc: Some(BSET_DESC),
        },
        Instruction {
            name: "btst",
            _desc: Some(BTST_DESC),
        },
        Instruction {
            name: "cmp",
            _desc: Some(CMP_DESC),
        },
        Instruction {
            name: "divu",
            _desc: Some(DIVS_DIVU_DESC),
        },
        Instruction {
            name: "divs",
            _desc: Some(DIVS_DIVU_DESC),
        },
        Instruction {
            name: "eor",
            _desc: Some(EOR_DESC),
        },
        Instruction {
            name: "exg",
            _desc: Some(EXG_DESC),
        },
        Instruction {
            name: "lea",
            _desc: Some(LEA_DESC),
        },
        Instruction {
            name: "move",
            _desc: Some(MOVE_DESC),
        },
        Instruction {
            name: "muls",
            _desc: Some(MULS_DESC),
        },
        Instruction {
            name: "mulu",
            _desc: Some(MULU_DESC),
        },
        Instruction {
            name: "or",
            _desc: Some(OR_DESC),
        },
        Instruction {
            name: "sub",
            _desc: Some(SUB_DESC),
        },
        Instruction {
            name: "subq",
            _desc: Some(SUBQ_DESC),
        },
        Instruction {
            name: "subx",
            _desc: Some(SUBX_DESC),
        },
        ];

    let inst_1_ops_000 = [
        Instruction {
            name: "clr",
            _desc: Some(CLR_DESC),
        },
        Instruction {
            name: "ext",
            _desc: Some(EXT_DESC),
        },
        Instruction {
            name: "bsr",
            _desc: Some(BSR_DESC),
        },
        Instruction {
            name: "jsr",
            _desc: Some(JSR_DESC),
        },
        Instruction {
            name: "jmp",
            _desc: Some(JMP_DESC),
        },
        Instruction {
            name: "neg",
            _desc: Some(NEG_DESC),
        },
        Instruction {
            name: "not",
            _desc: Some(NOT_DESC),
        },
        Instruction {
            name: "swap",
            _desc: Some(SWAP_DESC),
        },
        ];

    let inst_0_ops_000 = [
        Instruction {
            name: "nop",
            _desc: Some(NOP_DESC),
        },
        Instruction {
            name: "illegal",
            _desc: None,
        },
        Instruction {
            name: "rte",
            _desc: Some(RTS_DESC),
        },
        Instruction {
            name: "rts",
            _desc: Some(RTS_DESC),
        },
    ];

    for inst in &inst_2_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, false, Some(&src_types), &dest_types);
        generate_table(&name_long, true, Some(&src_types), &dest_types);
    }

    // Generate instructions with one op

    /*
    for inst in &inst_1_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, None, &dest_types);
        generate_table(&name_long, None, &dest_types);
    }

    //

    // Generate instructions with no ops

    for inst in &inst_0_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, None, &dest_types_none);
        generate_table(&name_long, None, &dest_types_none);
    }
    */
}

extern "C" {
    fn m68k_wrapper_init();
    fn m68k_run_instructions(instructions: *const raw::c_void, count: u32, cycle_res: *mut u32);
}

<commit_msg>Rename _desc -> desc<commit_after>extern crate rayon;

use rayon::prelude::*;

use std::process::Command;
use std::fs::File;
use std::io::Write;
use std::os::raw;
use std::io::Read;

pub mod descriptions;

use descriptions::*;

#[cfg(target_os="windows")]
const VASM_EXE: &'static str = "bin/win/vasmm68k_mot.exe";

#[cfg(target_os="macos")]
const VASM_EXE: &'static str = "bin/mac/vasmm68k_mot";

#[cfg(any(target_os="linux",
          target_os="freebsd",
          target_os="dragonfly",
          target_os="netbsd",
          target_os="openbsd"))]
const VASM_EXE: &'static str = "vasmm68k_mot";

struct BuildResult {
    src: Option<Op>,
    dst: Op,
    temp_file: String,
    temp_out: String,
    cycle_count: Option<usize>,
}

#[derive(Copy, Clone)]
struct Op {
    name: &'static str,
    print_name: &'static str,
}

impl Op {
    fn new(name: &'static str, print_name: &'static str) -> Op {
        Op {
            name: name,
            print_name: print_name,
        }
    }
}
struct Instruction {
    name: &'static str,
    desc: Option<Description>,
}

fn compile_statement(filename: &str, file_out: &str, statement: &str) -> bool {
    {
        let mut file = File::create(filename).unwrap();
        write!(file, " {}", statement).unwrap();
    }

    let output = Command::new(VASM_EXE)
        .arg("-no-opt")
        .arg("-m68000")
        .arg(filename)
        .arg("-Fbin")
        .arg("-o")
        .arg(file_out)
        .output()
        .expect("failed to execute process");

    output.status.success()
}

fn print_grid_table(name: &str, cycles: &Vec<BuildResult>, src_table: &[Op], dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    for src in src_table {
        let mut skip_count = 0;

        for i in index..index + 9 {
            if let None = cycles[i].cycle_count {
                skip_count += 1;
            }
        }

        if skip_count != 9 {
            print!("| {name:<width$}", name = src.print_name, width = 9);

            for dest in dest_table {
                if let Some(cycle_count) = cycles[index].cycle_count {
                    print!("|{number:^width$}",
                           number = cycle_count,
                           width = dest.print_name.len() + 2);
                } else {
                    print!("|{number:^width$}",
                           number = "*",
                           width = dest.print_name.len() + 2);
                }
                index += 1;
            }

            println!("|");
        } else {
            index += 9;
        }
    }

    println!("");
}

fn print_table(name: &str, cycles: &Vec<BuildResult>, dest_table: &[Op]) {
    print!("| {name:<width$}", name = name, width = 9);

    for dst in dest_table {
        print!("| {} ", dst.print_name);
    }

    println!("|");
    println!("|----------|----|----|------|-------|-------|-------|----------|-------|-------|");

    let mut index = 0;

    print!("| {name:<width$}", name = " ", width = 9);

    for dest in dest_table {
        if let Some(cycle_count) = cycles[index].cycle_count {
            print!("|{number:^width$}",
                   number = cycle_count,
                   width = dest.print_name.len() + 2);
        } else {
            print!("|{number:^width$}",
                   number = "*",
                   width = dest.print_name.len() + 2);
        }
        index += 1;
    }

    println!("|");
    println!("");
}

fn print_instruction_header(inst: &Instruction) {
    let name = inst.name.to_uppercase();
    println!("## {}\n", name);
    if let Some(ref desc) = inst.desc {
        println!("**Operation:**      {}\n", desc.operation);

        print!("**Assembler:** ");
        for assem in desc.assembler {
            print!("{}        ", assem);
        }
        println!("\n\n**Attributes:** Size = ({})\n", desc.attributes);

        println!("**Description:** {}\n", desc.description);
    }
    else {
        println!("__No Description__\n");
    }
}

fn compile_cycle_counts(statements: &mut Vec<BuildResult>) {
    let mut instructions = Vec::new();
    let mut inst_count = 0u32;
    let mut cycle_count = Vec::<u32>::new();

    for statement in statements.iter() {
        if statement.cycle_count.is_some() {
            {
                let mut f = File::open(&statement.temp_out).unwrap();
                f.read_to_end(&mut instructions).unwrap();
                cycle_count.push(0);
                inst_count += 1;
            }
        }
    }

    unsafe {
        m68k_run_instructions(instructions.as_ptr() as *const raw::c_void, inst_count, cycle_count.as_mut_ptr());
    }

    inst_count = 0;

    for statement in statements.iter_mut() {
        if statement.cycle_count.is_some() {
            statement.cycle_count = Some(cycle_count[inst_count as usize] as usize);
            inst_count += 1;
        }
    }
}

fn generate_table(name: &str,
                  is_long: bool,
                  src_table: Option<&[Op]>,
                  dest_table: &[Op]) {
    let mut statements = Vec::with_capacity(20 * 20);
    let mut count = 0;

    if let Some(src_opts) = src_table {
        for src in src_opts {
            for dst in dest_table {
                let file_in = format!("target/temp_{}.s", count);
                let file_out = format!("target/temp_{}.o", count);

                statements.push(BuildResult {
                    src: Some(src.clone()),
                    dst: dst.clone(),
                    temp_file: file_in,
                    temp_out: file_out,
                    cycle_count: None,
                });

                count += 1;
            }
        }

        statements.par_iter_mut().weight_max().for_each(|v| {
            let src = v.src.unwrap();
            let statement;

            if src.print_name == "#xxx" && is_long {
                statement = format!("{} {},{}", name, "#$ffffff", v.dst.name);
            } else {
                statement = format!("{} {},{}", name, v.src.unwrap().name, v.dst.name);
            }

            //println!("Statement {}", statement);
            if compile_statement(&v.temp_file, &v.temp_out, &statement) {
                v.cycle_count = Some(0); // indicate that this should be processed
            }
        });

        compile_cycle_counts(&mut statements);

        print_grid_table(name, &statements, &src_opts, dest_table);
    } else {
        for dst in dest_table {
            let file_in = format!("target/temp_{}.s", count);
            let file_out = format!("target/temp_{}.o", count);

            statements.push(BuildResult {
                src: None,
                dst: dst.clone(),
                temp_file: file_in,
                temp_out: file_out,
                cycle_count: None,
            });

            count += 1;
        }

        statements.par_iter_mut().weight_max().for_each(|v| {
            let statement = format!("{} {}", name, v.dst.name);
            if compile_statement(&v.temp_file, &v.temp_out, &statement) {
                v.cycle_count = Some(0);
            }
        });

        compile_cycle_counts(&mut statements);

        print_table(name, &statements, dest_table);
    }
}

fn main() {
    let dest_types = [
        Op::new("d0", "Dn"),
        Op::new("a0", "An"),
        Op::new("(a0)", "(An)"),
        Op::new("(a0)+", "(An)+"),
        Op::new("-(a0)", "-(An)"),
        Op::new("2(a0)", "d(An)"),
        Op::new("2(a0,d0)", "d(An,Dn)"),
        Op::new("$4.W", "xxx.W"),
        Op::new("$4.L", "xxx.L")];

    let src_types = [
        Op::new("d0", "Dn"),
        Op::new("a0", "An"),
        Op::new("(a0)", "(An)"),
        Op::new("(a0)+", "(An)+"),
        Op::new("-(a0)", "-(An)"),
        Op::new("2(a0)", "d(An)"),
        Op::new("2(a0,d0)", "d(An,Dn)"),
        Op::new("$4.W", "xxx.W"),
        Op::new("$4.L", "xxx.L"),
        Op::new("2(pc)", "d(PC)"),
        Op::new("2(pc,d0)", "d(PC,Dn)"),
        Op::new("#2000", "#xxx")];

    let dest_types_none = [Op::new("", "")];

    unsafe {
        m68k_wrapper_init();
    }

    let inst_2_ops_000 =
        [
        Instruction {
            name: "abcd",
            desc: Some(ABCD_DESC),
        },

        Instruction {
            name: "add",
            desc: Some(ADD_DESC),
        },
        Instruction {
            name: "addq",
            desc: Some(ADDQ_DESC),
        },
        Instruction {
            name: "addx",
            desc: Some(ADDX_DESC),
        },
        Instruction {
            name: "and",
            desc: Some(AND_DESC),
        },
        Instruction {
            name: "bchg",
            desc: Some(BCHG_DESC),
        },
        Instruction {
            name: "bclr",
            desc: Some(BCLR_DESC),
        },
        Instruction {
            name: "bset",
            desc: Some(BSET_DESC),
        },
        Instruction {
            name: "btst",
            desc: Some(BTST_DESC),
        },
        Instruction {
            name: "cmp",
            desc: Some(CMP_DESC),
        },
        Instruction {
            name: "divu",
            desc: Some(DIVS_DIVU_DESC),
        },
        Instruction {
            name: "divs",
            desc: Some(DIVS_DIVU_DESC),
        },
        Instruction {
            name: "eor",
            desc: Some(EOR_DESC),
        },
        Instruction {
            name: "exg",
            desc: Some(EXG_DESC),
        },
        Instruction {
            name: "lea",
            desc: Some(LEA_DESC),
        },
        Instruction {
            name: "move",
            desc: Some(MOVE_DESC),
        },
        Instruction {
            name: "muls",
            desc: Some(MULS_DESC),
        },
        Instruction {
            name: "mulu",
            desc: Some(MULU_DESC),
        },
        Instruction {
            name: "or",
            desc: Some(OR_DESC),
        },
        Instruction {
            name: "sub",
            desc: Some(SUB_DESC),
        },
        Instruction {
            name: "subq",
            desc: Some(SUBQ_DESC),
        },
        Instruction {
            name: "subx",
            desc: Some(SUBX_DESC),
        },
        ];

    let inst_1_ops_000 = [
        Instruction {
            name: "clr",
            desc: Some(CLR_DESC),
        },
        Instruction {
            name: "ext",
            desc: Some(EXT_DESC),
        },
        Instruction {
            name: "bsr",
            desc: Some(BSR_DESC),
        },
        Instruction {
            name: "jsr",
            desc: Some(JSR_DESC),
        },
        Instruction {
            name: "jmp",
            desc: Some(JMP_DESC),
        },
        Instruction {
            name: "neg",
            desc: Some(NEG_DESC),
        },
        Instruction {
            name: "not",
            desc: Some(NOT_DESC),
        },
        Instruction {
            name: "swap",
            desc: Some(SWAP_DESC),
        },
        ];

    let inst_0_ops_000 = [
        Instruction {
            name: "nop",
            desc: Some(NOP_DESC),
        },
        Instruction {
            name: "illegal",
            desc: None,
        },
        Instruction {
            name: "rte",
            desc: Some(RTS_DESC),
        },
        Instruction {
            name: "rts",
            desc: Some(RTS_DESC),
        },
    ];

    for inst in &inst_2_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, false, Some(&src_types), &dest_types);
        generate_table(&name_long, true, Some(&src_types), &dest_types);
    }

    // Generate instructions with one op

    /*
    for inst in &inst_1_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, None, &dest_types);
        generate_table(&name_long, None, &dest_types);
    }

    //

    // Generate instructions with no ops

    for inst in &inst_0_ops_000 {
        let name_long = format!("{}.l", inst.name);

        print_instruction_header(inst);
        generate_table(inst.name, None, &dest_types_none);
        generate_table(&name_long, None, &dest_types_none);
    }
    */
}

extern "C" {
    fn m68k_wrapper_init();
    fn m68k_run_instructions(instructions: *const raw::c_void, count: u32, cycle_res: *mut u32);
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate nickel;

use std::collections::HashMap;
use nickel::*;

fn main() {
   let mut nickel = Nickel::new();

   nickel.utilize(middleware! { |request|
     println!("Request: {:?}", request.origin.uri);
   });

   nickel.get("/", middleware! { |_req, res|
      let mut data = HashMap::<&str, &str>::new();
      data.insert("title", "nickel-bootstrap");
      data.insert("message", "Welcome to");
      data.insert("message_line_two", "You can edit this to get started...");
      return res.render("templates/index.mustache", &data)
   });

   nickel.utilize(StaticFilesHandler::new("public/"));

   nickel.listen("127.0.0.1:6767");
}
<commit_msg>fix dead_code warnings<commit_after>#[macro_use]
extern crate nickel;

use std::collections::HashMap;
use nickel::*;

pub fn main() {
   let mut nickel = Nickel::new();

   nickel.utilize(middleware! { |request|
     println!("Request: {:?}", request.origin.uri);
   });

   nickel.get("/", middleware! { |_req, res|
      let mut data = HashMap::<&str, &str>::new();
      data.insert("title", "nickel-bootstrap");
      data.insert("message", "Welcome to");
      data.insert("message_line_two", "You can edit this to get started...");
      return res.render("templates/index.mustache", &data)
   });

   nickel.utilize(StaticFilesHandler::new("public/"));

   nickel.listen("127.0.0.1:6767");
}
<|endoftext|>