<commit_before>use std::io::{self, BufReader, Write};
use std::io::BufRead;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;
use stats::median;

extern crate clap;
use clap::{Arg, App};

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(UTC::now().with_timezone(&r.time.timezone()) - time_filter.unwrap(), UTC::now().with_timezone(&r.time.timezone()))) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

pub fn render_graphite<T: Write>(result: RequestLogAnalyzerResult, time: DateTime<FixedOffset>, mut stream: T) {
    stream.write(
        format!(
            "count {} {:?}\n",
            result.count,
            time.timestamp()
        )
        .as_bytes()
    );
}

fn main() {
    let matches = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(true)
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .get_matches();

    let filename = matches.value_of("filename").unwrap();

    let time_filter = match matches.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses)
        .into_iter()
        .filter(|rr| rr.matches_include_exclude_filter(
            matches.value_of("include_term"),
            matches.value_of("exclude_term")
        ))
        .collect();

    match analyze(&pairs) {
        Some(result) => render_terminal(result),
        None => println!("No matching log lines in file.")
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate chrono;
    use chrono::*;

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }

    #[test]
    fn test_render_graphite() {
        use std::io::{self, BufReader};
        use std::io::prelude::Write;
        use std::str;

        struct MockTcpStream {
            write_calls: Vec<String>,
        };

        impl Write for MockTcpStream {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
                Ok(1)
            }

            fn flush(&mut self) -> io::Result<()> { Ok(()) }
        }

        let mut mock_tcp_stream = MockTcpStream {
            write_calls: vec![]
        };

        render_graphite(RequestLogAnalyzerResult {
                count: 3,
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            },
            DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200", "%d/%b/%Y:%H:%M:%S %z").unwrap(),
            &mut mock_tcp_stream
        );

        assert_eq!(&mock_tcp_stream.write_calls[0], "requests.count 3 1474576919\n");
    }
}
<commit_msg>`requests` prefix is designed for Graphite<commit_after>use std::io::{self, BufReader, Write};
use std::io::BufRead;
use std::fs::File;

extern crate chrono;
use chrono::*;

extern crate stats;
use stats::median;

extern crate clap;
use clap::{Arg, App};

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(UTC::now().with_timezone(&r.time.timezone()) - time_filter.unwrap(), UTC::now().with_timezone(&r.time.timezone()))) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

pub fn render_graphite<T: Write>(result: RequestLogAnalyzerResult, time: DateTime<FixedOffset>, mut stream: T) {
    stream.write(
        format!(
            "requests.count {} {:?}\n",
            result.count,
            time.timestamp()
        )
        .as_bytes()
    );
}

fn main() {
    let matches = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(true)
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
            .value_name("MINUTES")
            .short("t")
            .help("Limit to the last n minutes")
            .takes_value(true))
        .arg(Arg::with_name("include_term")
            .value_name("TERM")
            .long("include")
            .help("Only includes lines that contain this term")
            .takes_value(true))
        .arg(Arg::with_name("exclude_term")
            .value_name("TERM")
            .long("exclude")
            .help("Excludes lines that contain this term")
            .takes_value(true))
        .get_matches();

    let filename = matches.value_of("filename").unwrap();

    let time_filter = match matches.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses)
        .into_iter()
        .filter(|rr| rr.matches_include_exclude_filter(
            matches.value_of("include_term"),
            matches.value_of("exclude_term")
        ))
        .collect();

    match analyze(&pairs) {
        Some(result) => render_terminal(result),
        None => println!("No matching log lines in file.")
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate chrono;
    use chrono::*;

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }

    #[test]
    fn test_render_graphite() {
        use std::io::{self, BufReader};
        use std::io::prelude::Write;
        use std::str;

        struct MockTcpStream {
            write_calls: Vec<String>,
        };

        impl Write for MockTcpStream {
            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
                Ok(1)
            }

            fn flush(&mut self) -> io::Result<()> { Ok(()) }
        }

        let mut mock_tcp_stream = MockTcpStream {
            write_calls: vec![]
        };

        render_graphite(RequestLogAnalyzerResult {
                count: 3,
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            },
            DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200", "%d/%b/%Y:%H:%M:%S %z").unwrap(),
            &mut mock_tcp_stream
        );

        assert_eq!(&mock_tcp_stream.write_calls[0], "requests.count 3 1474576919\n");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::env;
use std::io;
use std::path::{Path, PathBuf};

extern crate clap;
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;

use clap::{App, Arg, ArgMatches};

mod bindgen;
mod logging;

use bindgen::{Bindings, Builder, Cargo, Config, Error, Language, Style};

fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
    // We allow specifying a language to override the config default. This is
    // used by compile-tests.
    if let Some(lang) = matches.value_of("lang") {
        config.language = match lang {
            "C++" => Language::Cxx,
            "c++" => Language::Cxx,
            "C" => Language::C,
            "c" => Language::C,
            _ => {
                error!("Unknown language specified.");
                return;
            }
        };
    }

    if matches.is_present("cpp-compat") {
        config.cpp_compat = true;
    }

    if let Some(style) = matches.value_of("style") {
        config.style = match style {
            "Both" => Style::Both,
            "both" => Style::Both,
            "Tag" => Style::Tag,
            "tag" => Style::Tag,
            "Type" => Style::Type,
            "type" => Style::Type,
            _ => {
                error!("Unknown style specified.");
                return;
            }
        }
    }

    if matches.is_present("d") {
        config.parse.parse_deps = true;
    }
}

fn load_bindings<'a>(input: &Path, matches: &ArgMatches<'a>) -> Result<Bindings, Error> {
    // If a file is specified then we load it as a single source
    if !input.is_dir() {
        // Load any config specified or search in the input directory
        let mut config = match matches.value_of("config") {
            Some(c) => Config::from_file(c).unwrap(),
            None => Config::from_root_or_default(input),
        };

        apply_config_overrides(&mut config, &matches);

        return Builder::new()
            .with_config(config)
            .with_src(input)
            .generate();
    }

    // We have to load a whole crate, so we use cargo to gather metadata
    let lib = Cargo::load(
        input,
        matches.value_of("lockfile"),
        matches.value_of("crate"),
        true,
        matches.is_present("clean"),
    )?;

    // Load any config specified or search in the binding crate directory
    let mut config = match matches.value_of("config") {
        Some(c) => Config::from_file(c).unwrap(),
        None => {
            let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref());

            if let Some(binding_crate_dir) = binding_crate_dir {
                Config::from_root_or_default(&binding_crate_dir)
            } else {
                // This shouldn't happen
                Config::from_root_or_default(input)
            }
        }
    };

    apply_config_overrides(&mut config, &matches);

    Builder::new()
        .with_config(config)
        .with_cargo(lib)
        .generate()
}

fn main() {
    let matches = App::new("cbindgen")
        .version(bindgen::VERSION)
        .about("Generate C bindings for a Rust library")
        .arg(
            Arg::with_name("v")
                .short("v")
                .multiple(true)
                .help("Enable verbose logging"),
        )
        .arg(
            Arg::with_name("verify")
                .long("verify")
                .help("Generate bindings and compare it to the existing bindings file and error if they are different"),
        )
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("PATH")
                .help("Specify path to a `cbindgen.toml` config to use"),
        )
        .arg(
            Arg::with_name("lang")
                .short("l")
                .long("lang")
                .value_name("LANGUAGE")
                .help("Specify the language to output bindings in")
                .possible_values(&["c++", "C++", "c", "C"]),
        )
        .arg(
            Arg::with_name("cpp-compat")
                .long("cpp-compat")
                .help("Whether to add C++ compatibility to generated C bindings")
        )
        .arg(
            Arg::with_name("style")
                .short("s")
                .long("style")
                .value_name("STYLE")
                .help("Specify the declaration style to use for bindings")
                .possible_values(&["Both", "both", "Tag", "tag", "Type", "type"]),
        )
        .arg(
            Arg::with_name("d")
                .short("d")
                .long("parse-dependencies")
                .help("Whether to parse dependencies when generating bindings"),
        )
        .arg(
            Arg::with_name("clean")
                .long("clean")
                .help(
                    "Whether to use a new temporary directory for expanding macros. \
                    Affects performance, but might be required in certain build processes.")
                .required(false)
        )
        .arg(
            Arg::with_name("INPUT")
                .help(
                    "A crate directory or source file to generate bindings for. \
                    In general this is the folder where the Cargo.toml file of \
                    source Rust library resides.")
                .required(false)
                .index(1),
        )
        .arg(
            Arg::with_name("crate")
                .long("crate")
                .value_name("CRATE_NAME")
                .help(
                    "If generating bindings for a crate, \
                     the specific crate to generate bindings for",
                )
                .required(false),
        )
        .arg(
            Arg::with_name("out")
                .short("o")
                .long("output")
                .value_name("PATH")
                .help("The file to output the bindings to")
                .required(false),
        )
        .arg(
            Arg::with_name("lockfile")
                .long("lockfile")
                .value_name("PATH")
                .help(
                    "Specify the path to the Cargo.lock file explicitly. If this \
                    is not specified, the Cargo.lock file is searched for in the \
                    same folder as the Cargo.toml file. This option is useful for \
                    projects that use workspaces.")
                .required(false),
        )
        .arg(
            Arg::with_name("quiet")
                .short("q")
                .long("quiet")
                .help("Report errors only.")
                .required(false),
        )
        .get_matches();

    if !matches.is_present("out") && matches.is_present("verify") {
        error!(
            "Cannot verify bindings against `stdout`, please specify a file to compare against."
        );
        std::process::exit(2);
    }

    // Initialize logging
    if matches.is_present("quiet") {
        logging::ErrorLogger::init().unwrap();
    } else {
        match matches.occurrences_of("v") {
            0 => logging::WarnLogger::init().unwrap(),
            1 => logging::InfoLogger::init().unwrap(),
            _ => logging::TraceLogger::init().unwrap(),
        }
    }

    // Find the input directory
    let input = match matches.value_of("INPUT") {
        Some(input) => PathBuf::from(input),
        None => env::current_dir().unwrap(),
    };

    let bindings = match load_bindings(&input, &matches) {
        Ok(bindings) => bindings,
        Err(msg) => {
            error!("{}", msg);
            error!("Couldn't generate bindings for {}.", input.display());
            std::process::exit(1);
        }
    };

    // Write the bindings file
    match matches.value_of("out") {
        Some(file) => {
            let changed = bindings.write_to_file(file);

            if matches.is_present("verify") && changed {
                std::process::exit(2);
            }
        }
        _ => {
            bindings.write(io::stdout());
        }
    }
}
<commit_msg>Note that --quiet overrides -v<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::env;
use std::io;
use std::path::{Path, PathBuf};

extern crate clap;
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;

use clap::{App, Arg, ArgMatches};

mod bindgen;
mod logging;

use bindgen::{Bindings, Builder, Cargo, Config, Error, Language, Style};

fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
    // We allow specifying a language to override the config default. This is
    // used by compile-tests.
    if let Some(lang) = matches.value_of("lang") {
        config.language = match lang {
            "C++" => Language::Cxx,
            "c++" => Language::Cxx,
            "C" => Language::C,
            "c" => Language::C,
            _ => {
                error!("Unknown language specified.");
                return;
            }
        };
    }

    if matches.is_present("cpp-compat") {
        config.cpp_compat = true;
    }

    if let Some(style) = matches.value_of("style") {
        config.style = match style {
            "Both" => Style::Both,
            "both" => Style::Both,
            "Tag" => Style::Tag,
            "tag" => Style::Tag,
            "Type" => Style::Type,
            "type" => Style::Type,
            _ => {
                error!("Unknown style specified.");
                return;
            }
        }
    }

    if matches.is_present("d") {
        config.parse.parse_deps = true;
    }
}

fn load_bindings<'a>(input: &Path, matches: &ArgMatches<'a>) -> Result<Bindings, Error> {
    // If a file is specified then we load it as a single source
    if !input.is_dir() {
        // Load any config specified or search in the input directory
        let mut config = match matches.value_of("config") {
            Some(c) => Config::from_file(c).unwrap(),
            None => Config::from_root_or_default(input),
        };

        apply_config_overrides(&mut config, &matches);

        return Builder::new()
            .with_config(config)
            .with_src(input)
            .generate();
    }

    // We have to load a whole crate, so we use cargo to gather metadata
    let lib = Cargo::load(
        input,
        matches.value_of("lockfile"),
        matches.value_of("crate"),
        true,
        matches.is_present("clean"),
    )?;

    // Load any config specified or search in the binding crate directory
    let mut config = match matches.value_of("config") {
        Some(c) => Config::from_file(c).unwrap(),
        None => {
            let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref());

            if let Some(binding_crate_dir) = binding_crate_dir {
                Config::from_root_or_default(&binding_crate_dir)
            } else {
                // This shouldn't happen
                Config::from_root_or_default(input)
            }
        }
    };

    apply_config_overrides(&mut config, &matches);

    Builder::new()
        .with_config(config)
        .with_cargo(lib)
        .generate()
}

fn main() {
    let matches = App::new("cbindgen")
        .version(bindgen::VERSION)
        .about("Generate C bindings for a Rust library")
        .arg(
            Arg::with_name("v")
                .short("v")
                .multiple(true)
                .help("Enable verbose logging"),
        )
        .arg(
            Arg::with_name("verify")
                .long("verify")
                .help("Generate bindings and compare it to the existing bindings file and error if they are different"),
        )
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("PATH")
                .help("Specify path to a `cbindgen.toml` config to use"),
        )
        .arg(
            Arg::with_name("lang")
                .short("l")
                .long("lang")
                .value_name("LANGUAGE")
                .help("Specify the language to output bindings in")
                .possible_values(&["c++", "C++", "c", "C"]),
        )
        .arg(
            Arg::with_name("cpp-compat")
                .long("cpp-compat")
                .help("Whether to add C++ compatibility to generated C bindings")
        )
        .arg(
            Arg::with_name("style")
                .short("s")
                .long("style")
                .value_name("STYLE")
                .help("Specify the declaration style to use for bindings")
                .possible_values(&["Both", "both", "Tag", "tag", "Type", "type"]),
        )
        .arg(
            Arg::with_name("d")
                .short("d")
                .long("parse-dependencies")
                .help("Whether to parse dependencies when generating bindings"),
        )
        .arg(
            Arg::with_name("clean")
                .long("clean")
                .help(
                    "Whether to use a new temporary directory for expanding macros. \
                    Affects performance, but might be required in certain build processes.")
                .required(false)
        )
        .arg(
            Arg::with_name("INPUT")
                .help(
                    "A crate directory or source file to generate bindings for. \
                    In general this is the folder where the Cargo.toml file of \
                    source Rust library resides.")
                .required(false)
                .index(1),
        )
        .arg(
            Arg::with_name("crate")
                .long("crate")
                .value_name("CRATE_NAME")
                .help(
                    "If generating bindings for a crate, \
                     the specific crate to generate bindings for",
                )
                .required(false),
        )
        .arg(
            Arg::with_name("out")
                .short("o")
                .long("output")
                .value_name("PATH")
                .help("The file to output the bindings to")
                .required(false),
        )
        .arg(
            Arg::with_name("lockfile")
                .long("lockfile")
                .value_name("PATH")
                .help(
                    "Specify the path to the Cargo.lock file explicitly. If this \
                    is not specified, the Cargo.lock file is searched for in the \
                    same folder as the Cargo.toml file. This option is useful for \
                    projects that use workspaces.")
                .required(false),
        )
        .arg(
            Arg::with_name("quiet")
                .short("q")
                .long("quiet")
                .help("Report errors only (overrides verbosity options).")
                .required(false),
        )
        .get_matches();

    if !matches.is_present("out") && matches.is_present("verify") {
        error!(
            "Cannot verify bindings against `stdout`, please specify a file to compare against."
        );
        std::process::exit(2);
    }

    // Initialize logging
    if matches.is_present("quiet") {
        logging::ErrorLogger::init().unwrap();
    } else {
        match matches.occurrences_of("v") {
            0 => logging::WarnLogger::init().unwrap(),
            1 => logging::InfoLogger::init().unwrap(),
            _ => logging::TraceLogger::init().unwrap(),
        }
    }

    // Find the input directory
    let input = match matches.value_of("INPUT") {
        Some(input) => PathBuf::from(input),
        None => env::current_dir().unwrap(),
    };

    let bindings = match load_bindings(&input, &matches) {
        Ok(bindings) => bindings,
        Err(msg) => {
            error!("{}", msg);
            error!("Couldn't generate bindings for {}.", input.display());
            std::process::exit(1);
        }
    };

    // Write the bindings file
    match matches.value_of("out") {
        Some(file) => {
            let changed = bindings.write_to_file(file);

            if matches.is_present("verify") && changed {
                std::process::exit(2);
            }
        }
        _ => {
            bindings.write(io::stdout());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Björn Edström <be@bjrn.se> 2015.
// See LICENSE for details.

extern crate chrono;
extern crate crypto;
extern crate getopts;
extern crate num;
extern crate rustc_serialize;

mod timmy;

use chrono::datetime::DateTime;
use chrono::offset::utc::UTC;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use getopts::Options;
use num::bigint::BigUint;
use rustc_serialize::base64::{STANDARD, ToBase64, FromBase64};
use rustc_serialize::json::{self};
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;

use timmy::rsa::*;
use timmy::util::*;
use timmy::tls::*;
use timmy::x509::*;

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct JsonOutput {
    blob: String,
    certificates: Vec<String>,
    signature: String
}

fn verify(blob: &Vec<u8>, cert0: &Vec<u8>, signature: &Vec<u8>) -> isize {
    // Parse certificates
    let cert = X509Certificate::new(cert0.clone());
    let parsed_cert = cert.parse().unwrap(); // XXX unwrap

    // Check if certificate expired.
    let utc_now: DateTime<UTC> = UTC::now();
    let cert_still_valid = utc_now >= parsed_cert.validity.0 && utc_now <= parsed_cert.validity.1;
    if !cert_still_valid {
        println_stderr!("ERROR! Signature verification FAILURE: Certificate has expired.");
        return 1;
    }

    match parsed_cert.key {
        PublicKey::RSA(rsa_n, rsa_e) => {

            // Perform RSA signature operation
            let signature_int = BigUint::from_bytes_be(&signature);
            let sig_op = rsa_encrypt(&signature_int, &rsa_e,  &rsa_n);

            // Fix up PKCS1.5 blob
            let mut raw_pkcs1_ = sig_op.to_bytes_be();
            raw_pkcs1_.insert(0, 0x00);
            let raw_pkcs1 = raw_pkcs1_;

            // Construct our own PKCS 1.5 blob to compare against
            let tls_hash = weird_tls_hash(&blob);
            let constructed_pkcs1 = make_pkcs1_sig_padding(&rsa_n, &tls_hash);

            let valid_signature = crypto_compare(&constructed_pkcs1, &raw_pkcs1);

            if valid_signature {
                // Signature is valid, parse out fields from blob.
                let mut tls = TLSHandshake::new();
                tls.client_random = blob[0..32].iter().cloned().collect();
                tls.server_random = blob[32..64].iter().cloned().collect();

                // Validate signed timestamp against certificate validity period.
                let unix_timestamp = tls.get_unix_timestamp();
                let ts = timestamp_to_datetime(unix_timestamp);
                let valid_dates = ts >= parsed_cert.validity.0 && ts <= parsed_cert.validity.1;

                if valid_dates {
                    println!("Signature verification SUCCESS.");
                    println!("Warning! Signature only verified against first X509 certificate.");
                    println!("Please verify yourself that the certificate chain is valid.");
                    println!("");
                    println!("{} Signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                             parsed_cert.subject,
                             to_hex_string(&tls.client_random), ts, unix_timestamp);
                } else {
                    println!("ERROR! Signature verification FAILURE: Invalid timestamp.");
                    return 1;
                }

            } else {
                println!("ERROR! Signature verification FAILURE.");
                return 1;
            }
        }
    }

    0
}

fn sign(server: &String, port: &u16, hash_buf: &[u8; 32]) -> isize {
    // Construct our special ClientHello message and send it to the server.
    let conn_str = format!("{}:{}", server, port);
    let mut tcpconn = TcpStream::connect(&conn_str[..]).unwrap();

    let mut ch = SimpleBinaryWriter::new();
    create_special_client_hello(&mut ch, &hash_buf);

    match tcpconn.write(&ch.buf) {
        Err(_) => {
            println_stderr!("ERROR! Failed to write to server.");
            return 1;
        }
        Ok(_) => {}
    }

    // Parse the response.
    let mut pars = BinaryParser::new(&mut tcpconn);
    let mut tls = TLSHandshake::new();

    tls.client_random.extend(hash_buf[0..32].iter());

    tls.parse_server_hello(&mut pars);
    tls.parse_certificates(&mut pars);
    tls.parse_server_key_exchange(&mut pars);

    // Extract timestamp and perform checks on it.
    let unix_timestamp = tls.get_unix_timestamp();
    let ts = timestamp_to_datetime(unix_timestamp);

    let utc_now: DateTime<UTC> = UTC::now();
    let utc_now_ts = utc_now.timestamp() as u32;

    if unix_timestamp < utc_now_ts - 3600 || unix_timestamp > utc_now_ts + 3600 {
        println_stderr!("ERROR! Server responded with invalid time! Aborting.");
        return 1;
    }

    // Output banner and result JSON blob.
    println_stderr!("{} signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                    server, to_hex_string(&hash_buf[0..32].iter().cloned().collect()), ts, unix_timestamp);

    let json_output = JsonOutput {
        blob: tls.signed_blob.to_base64(STANDARD),
        signature: tls.signature.to_base64(STANDARD),
        certificates: tls.certs.iter().map(
            |c| c.buf.to_base64(STANDARD)
            ).collect(),
    };

    println!("{}", json::encode(&json_output).unwrap());

    0
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

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

    opts.optopt("v", "verify", "verify signature object", "PATH");
    opts.optopt("f", "file", "input file to sign [-]", "PATH");
    opts.optopt("s", "server", "set server for signing [www.google.com]", "HOSTNAME");
    opts.optopt("p", "port", "set port for --server [443]", "PORT");

    opts.optflag("h", "help", "print this help menu");

    let parse_result = opts.parse(&args[1..]);

    if let Err(f) = parse_result {
        println_stderr!("ERROR: {}", f.to_string());
        return;
    }

    let matches = parse_result.unwrap();

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    // parse "server"
    let server = if matches.opt_present("s") {
        matches.opt_str("s").unwrap()
    } else {
        "www.google.com".to_string()
    };

    // parse "port"
    let port: u16 = if matches.opt_present("p") {
        let port_str = matches.opt_str("p").unwrap();
        match port_str.parse() {
            Ok(p) => p,
            Err(_) => {
                print_usage(&program, opts);
                return;
            }
        }
    } else {
        443
    };

    if !matches.opt_present("v") {
        // sign

        // parse "file" and hash it
        let mut hasher = Sha256::new();

        if matches.opt_present("f") {
            let path = matches.opt_str("f").unwrap();
            let mut file_handle = match File::open(path) {
                Ok(f) => f,
                Err(s) => {
                    println!("Reading file failed: {}", s);
                    //print_usage(&program, opts);
                    return;
                }
            };

            hash_content(&mut file_handle, &mut hasher);
        } else {
            hash_content(&mut io::stdin(), &mut hasher);
        };

        let mut hash_buf: [u8; 32] = [0; 32];
        hasher.result(&mut hash_buf);

        sign(&server, &port, &hash_buf);
    } else {
        // verify

        let path = matches.opt_str("v").unwrap();
        let mut contents: Vec<u8> = Vec::new();
        let file_handle = File::open(path) ;
        file_handle.unwrap().read_to_end(&mut contents).unwrap();

        let filestr = String::from_utf8(contents).unwrap();

        let json_blob: JsonOutput = json::decode(&filestr).unwrap();

        let blob = json_blob.blob.from_base64().unwrap();
        let cert0 = json_blob.certificates[0].from_base64().unwrap();
        let signature = json_blob.signature.from_base64().unwrap();

        verify(&blob, &cert0, &signature);
    }
}
<commit_msg>Set exit status on success/failure.<commit_after>// Copyright (c) Björn Edström <be@bjrn.se> 2015.
// See LICENSE for details.

extern crate chrono;
extern crate crypto;
extern crate getopts;
extern crate num;
extern crate rustc_serialize;

mod timmy;

use chrono::datetime::DateTime;
use chrono::offset::utc::UTC;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use getopts::Options;
use num::bigint::BigUint;
use rustc_serialize::base64::{STANDARD, ToBase64, FromBase64};
use rustc_serialize::json::{self};
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io;
use std::net::TcpStream;
use std::process::exit;

use timmy::rsa::*;
use timmy::util::*;
use timmy::tls::*;
use timmy::x509::*;

macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct JsonOutput {
    blob: String,
    certificates: Vec<String>,
    signature: String
}

fn verify(blob: &Vec<u8>, cert0: &Vec<u8>, signature: &Vec<u8>) -> i32 {
    // Parse certificates
    let cert = X509Certificate::new(cert0.clone());
    let parsed_cert = cert.parse().unwrap(); // XXX unwrap

    // Check if certificate expired.
    let utc_now: DateTime<UTC> = UTC::now();
    let cert_still_valid = utc_now >= parsed_cert.validity.0 && utc_now <= parsed_cert.validity.1;
    if !cert_still_valid {
        println_stderr!("ERROR! Signature verification FAILURE: Certificate has expired.");
        return 1;
    }

    match parsed_cert.key {
        PublicKey::RSA(rsa_n, rsa_e) => {

            // Perform RSA signature operation
            let signature_int = BigUint::from_bytes_be(&signature);
            let sig_op = rsa_encrypt(&signature_int, &rsa_e,  &rsa_n);

            // Fix up PKCS1.5 blob
            let mut raw_pkcs1_ = sig_op.to_bytes_be();
            raw_pkcs1_.insert(0, 0x00);
            let raw_pkcs1 = raw_pkcs1_;

            // Construct our own PKCS 1.5 blob to compare against
            let tls_hash = weird_tls_hash(&blob);
            let constructed_pkcs1 = make_pkcs1_sig_padding(&rsa_n, &tls_hash);

            let valid_signature = crypto_compare(&constructed_pkcs1, &raw_pkcs1);

            if valid_signature {
                // Signature is valid, parse out fields from blob.
                let mut tls = TLSHandshake::new();
                tls.client_random = blob[0..32].iter().cloned().collect();
                tls.server_random = blob[32..64].iter().cloned().collect();

                // Validate signed timestamp against certificate validity period.
                let unix_timestamp = tls.get_unix_timestamp();
                let ts = timestamp_to_datetime(unix_timestamp);
                let valid_dates = ts >= parsed_cert.validity.0 && ts <= parsed_cert.validity.1;

                if valid_dates {
                    println!("Signature verification SUCCESS.");
                    println!("Warning! Signature only verified against first X509 certificate.");
                    println!("Please verify yourself that the certificate chain is valid.");
                    println!("");
                    println!("{} Signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                             parsed_cert.subject,
                             to_hex_string(&tls.client_random), ts, unix_timestamp);
                } else {
                    println!("ERROR! Signature verification FAILURE: Invalid timestamp.");
                    return 1;
                }

            } else {
                println!("ERROR! Signature verification FAILURE.");
                return 1;
            }
        }
    }

    0
}

fn sign(server: &String, port: &u16, hash_buf: &[u8; 32]) -> i32 {
    // Construct our special ClientHello message and send it to the server.
    let conn_str = format!("{}:{}", server, port);
    let mut tcpconn = TcpStream::connect(&conn_str[..]).unwrap();

    let mut ch = SimpleBinaryWriter::new();
    create_special_client_hello(&mut ch, &hash_buf);

    match tcpconn.write(&ch.buf) {
        Err(_) => {
            println_stderr!("ERROR! Failed to write to server.");
            return 1;
        }
        Ok(_) => {}
    }

    // Parse the response.
    let mut pars = BinaryParser::new(&mut tcpconn);
    let mut tls = TLSHandshake::new();

    tls.client_random.extend(hash_buf[0..32].iter());

    tls.parse_server_hello(&mut pars);
    tls.parse_certificates(&mut pars);
    tls.parse_server_key_exchange(&mut pars);

    // Extract timestamp and perform checks on it.
    let unix_timestamp = tls.get_unix_timestamp();
    let ts = timestamp_to_datetime(unix_timestamp);

    let utc_now: DateTime<UTC> = UTC::now();
    let utc_now_ts = utc_now.timestamp() as u32;

    if unix_timestamp < utc_now_ts - 3600 || unix_timestamp > utc_now_ts + 3600 {
        println_stderr!("ERROR! Server responded with invalid time! Aborting.");
        return 1;
    }

    // Output banner and result JSON blob.
    println_stderr!("{} signed SHA-256 {} at {:?} (Unix Timestamp: {})",
                    server, to_hex_string(&hash_buf[0..32].iter().cloned().collect()), ts, unix_timestamp);

    let json_output = JsonOutput {
        blob: tls.signed_blob.to_base64(STANDARD),
        signature: tls.signature.to_base64(STANDARD),
        certificates: tls.certs.iter().map(
            |c| c.buf.to_base64(STANDARD)
            ).collect(),
    };

    println!("{}", json::encode(&json_output).unwrap());

    0
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

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

    opts.optopt("v", "verify", "verify signature object", "PATH");
    opts.optopt("f", "file", "input file to sign [-]", "PATH");
    opts.optopt("s", "server", "set server for signing [www.google.com]", "HOSTNAME");
    opts.optopt("p", "port", "set port for --server [443]", "PORT");

    opts.optflag("h", "help", "print this help menu");

    let parse_result = opts.parse(&args[1..]);

    if let Err(f) = parse_result {
        println_stderr!("ERROR: {}", f.to_string());
        return;
    }

    let matches = parse_result.unwrap();

    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    // parse "server"
    let server = if matches.opt_present("s") {
        matches.opt_str("s").unwrap()
    } else {
        "www.google.com".to_string()
    };

    // parse "port"
    let port: u16 = if matches.opt_present("p") {
        let port_str = matches.opt_str("p").unwrap();
        match port_str.parse() {
            Ok(p) => p,
            Err(_) => {
                print_usage(&program, opts);
                return;
            }
        }
    } else {
        443
    };

    if !matches.opt_present("v") {
        // sign

        // parse "file" and hash it
        let mut hasher = Sha256::new();

        if matches.opt_present("f") {
            let path = matches.opt_str("f").unwrap();
            let mut file_handle = match File::open(path) {
                Ok(f) => f,
                Err(s) => {
                    println_stderr!("Reading file failed: {}", s);
                    exit(1);
                }
            };

            hash_content(&mut file_handle, &mut hasher);
        } else {
            hash_content(&mut io::stdin(), &mut hasher);
        };

        let mut hash_buf: [u8; 32] = [0; 32];
        hasher.result(&mut hash_buf);

        let ret = sign(&server, &port, &hash_buf);

        exit(ret);
    } else {
        // verify

        let path = matches.opt_str("v").unwrap();
        let mut contents: Vec<u8> = Vec::new();
        let file_handle = File::open(path) ;
        file_handle.unwrap().read_to_end(&mut contents).unwrap();

        let filestr = String::from_utf8(contents).unwrap();

        let json_blob: JsonOutput = json::decode(&filestr).unwrap();

        let blob = json_blob.blob.from_base64().unwrap();
        let cert0 = json_blob.certificates[0].from_base64().unwrap();
        let signature = json_blob.signature.from_base64().unwrap();

        let ret = verify(&blob, &cert0, &signature);

        exit(ret);
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;
use docopt::Docopt;

use std::env;
use std::fs;
use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::exit;

extern crate liquid;
use liquid::Context;
use liquid::Renderable;
use liquid::Template;
use liquid::Value;

// use liquid::{Renderable, LiquidOptions, Context, Value};

extern crate walkdir;
use walkdir::WalkDir;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const USAGE: &'static str = "
Usage: limpet [options] <source> <dest>
       limpet [options] --recursive <dir>
       limpet (--help | --version)

Options:
    -h, --help              Show this message.
    -v, --version           Show the version of limpet.
    -r, --recursive         Scan for files with extension from given directory.  Outputs to same directory.
    -e, --extension=EXT     Override the default extention for recursive scans [default: liquid].
";

#[derive(RustcDecodable)]
struct Args {
    arg_source: String,
    arg_dest: String,
    flag_extension: String,
    flag_recursive: bool,
    arg_dir: String,
    flag_help: bool,
    flag_version: bool
}

fn read_whole_file(mut f: &File) -> String {
    let mut contents: Vec<u8> = Vec::new();
    let size = f.read_to_end(&mut contents).expect("Couldn't read whole file");
    println!("Read {} bytes", size);
    return String::from_utf8(contents).unwrap();
}

fn main() {
    let args:Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    // Build a data context for the liquid templates
    let mut data = Context::new();
    for (key, value) in env::vars() {
        // println!("key: {} value: {}", &key, &value);
        data.set_val(&key, Value::Str(value));
    }

    if args.flag_version {
        println!("limpet v{}", VERSION);
        exit(0);
    }

    if args.flag_recursive {
        // Do recursive scan starting at <dir>
        for entry in WalkDir::new(&args.arg_dir) {
            match entry {
                Ok(ref e) => {
                    // println!("scanning {} ", e.path().display());
                    if e.file_type().is_file() {
                        let p = e.path();
                        // println!("{} is a file", p.display());
                        if let Some(ext) = p.extension() {
                            if ext.to_str() == Some(&args.flag_extension) {
                                if let Some(parent) = p.parent() {
                                    let mut path = PathBuf::from(parent);
                                    if let Some(file_stem) = p.file_stem() {
                                        path.push(file_stem);
                                        let dest = path.as_path();
                                        println!("Processing file {} into {}", p.display(), dest.display());
                                    }
                                }
                            }
                        }
                    }
                }
                Err(_) => {
                }
            }
            // let entry = entry.unwrap();
            // println!("{}", entry.path().display());
        }
    } else {
        // Do single file
        match File::open(&args.arg_source) {
            Ok(source) => {
                if let Ok(metadata) = source.metadata() {
                    if metadata.is_dir() {
                        println!("Source file {} can't be a directory.  Did you mean to use the --recursive option?", &args.arg_source);
                        exit(1);
                    }

                    // TODO: save permissions to set file with later?
                    // let permissions = metadata.permissions();

                } else {
                    println!("Problem reading source file metadata <{}>: Could be permissions.", &args.arg_source);
                    exit(1);
                }

                // Read in full template as string
                let template_string = read_whole_file(&source);
                let template: Template = liquid::parse(&template_string, Default::default()).unwrap();
                if let Ok(Some(output_string)) = template.render(&mut data) {
                    // println!("rendered file is {:?}", &output_string);
                    match File::create(&args.arg_dest) {
                        Ok(mut destination) => {
                            if let Err(e) = destination.write_all(&output_string.into_bytes()) {
                                println!("Error writing file {}", &args.arg_dest);
                                exit(1);
                            }
                            destination.flush();
                        }
                        Err(e) => {
                            println!("Destination file <{}>: {}", &args.arg_dest, &e);
                            exit(1);
                        }
                    }
                } else {
                    println!("Error rendering the template");
                    exit(1);
                }
            }
            Err(e) => {
                println!("Source file <{}>: {}", &args.arg_source, &e);
                exit(1);
            }
        }
    }
}
<commit_msg>save for night<commit_after>extern crate docopt;
extern crate rustc_serialize;
extern crate liquid;
extern crate walkdir;

use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;
use std::process::exit;
use docopt::Docopt;
use liquid::Context;
use liquid::Renderable;
use liquid::Template;
use liquid::Value;
use walkdir::WalkDir;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const USAGE: &'static str = "
Usage: limpet [options] <source> <dest>
       limpet [options] --recursive <dir>
       limpet (--help | --version)

Options:
    -h, --help              Show this message.
    -v, --version           Show the version of limpet.
    -r, --recursive         Scan for files with extension from given directory.  Outputs to same directory.
    -e, --extension=EXT     Override the default extention for recursive scans [default: liquid].
";

#[derive(RustcDecodable)]
struct Args {
    arg_source: String,
    arg_dest: String,
    flag_extension: String,
    flag_recursive: bool,
    arg_dir: String,
    // flag_help: bool,  Don't need this - it's automatically processed by docopt
    flag_version: bool
}

fn process_single_file(source: &PathBuf, destination: &PathBuf, context: &mut Context) -> Result<(), String> {
    // Read in full template as string
    match File::open(source.as_path()) {
        Ok(mut f) => {
            // Double check that the source isn't a directory
            if let Ok(metadata) = source.metadata() {
                if metadata.is_dir() {
                    return Err(format!("Source file {:?} can't be a directory.  Did you mean to use the --recursive option?", &source));
                }

                // TODO: save permissions to set file with later?
                // let permissions = metadata.permissions();

            } else {
                return Err(format!("Problem reading source file metadata <{:?}>: Could be permissions.", &source));
            }

            // Read template and process
            let mut contents: Vec<u8> = Vec::new();
            let _ = f.read_to_end(&mut contents).expect("Couldn't read whole file");
            // println!("Read {} bytes", size);
            let template_string = String::from_utf8(contents).unwrap();
            let template: Template = liquid::parse(&template_string, Default::default()).unwrap();
            if let Ok(Some(output_string)) = template.render(context) {
                // println!("rendered file is {:?}", &output_string);
                match File::create(&destination) {
                    Ok(mut output_file) => {
                        if let Err(e) = output_file.write_all(&output_string.into_bytes()) {
                            return Err(format!("Error writing file {}: {}", destination.to_str().unwrap(), &e));
                        }
                        // I don't think we need to flush/close in rust.
                        // output_file.flush();
                        return Ok(());
                    }
                    Err(e) => {
                        return Err(format!("Destination file <{}>: {}", destination.to_str().unwrap(), &e));
                    }
                }
            } else {
                return Err(format!("Unknown error while rendering the template for {}", source.to_str().unwrap()));
            }
        }
        Err(e) => {
            return Err(format!("{:?}", e));
        }
    }
}

fn main() {
    let args:Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    // Build a data context for the liquid templates
    let mut data = Context::new();
    for (key, value) in env::vars() {
        // println!("key: {} value: {}", &key, &value);
        data.set_val(&key, Value::Str(value));
    }

    if args.flag_version {
        println!("limpet v{}", VERSION);
        exit(0);
    }

    if args.flag_recursive {
        // Do recursive scan starting at <dir>
        println!("Scanning directory {}...", &args.arg_dir);
        for entry in WalkDir::new(&args.arg_dir) {
            match entry {
                Ok(ref e) => {
                    // println!("Scanning directory {} ", e.path().display());
                    if e.file_type().is_file() {
                        let p = e.path();
                        // println!("{} is a file", p.display());
                        if let Some(ext) = p.extension() {
                            if ext.to_str() == Some(&args.flag_extension) {
                                if let Some(parent) = p.parent() {
                                    let mut dest = PathBuf::from(parent);
                                    if let Some(file_stem) = p.file_stem() {
                                        dest.push(file_stem);
                                        // println!("Processing file {} into {}", p.display(), dest.display());
                                        match process_single_file(&p.to_path_buf(), &dest, &mut data) {
                                            Ok(_) => {
                                                println!("Processed: {} => {}", p.to_path_buf().display(), dest.display());
                                            }
                                            Err(e) => {
                                                println!("ERROR processing file {}: {:?}", p.display(), e);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(ref e) => {
                    println!("ERROR processing directory entry {:?}: {:?}", entry, e);
                }
            }
        }
    } else {
        // Do single file
        let source = PathBuf::from(&args.arg_source);
        let dest = PathBuf::from(&args.arg_dest);
        match process_single_file(&source.to_path_buf(), &dest, &mut data) {
            Ok(_) => {
                println!("Processed: {} => {}", source.display(), dest.display());
                exit(0);
            }
            Err(e) => {
                println!("ERROR processing file {}: {:?}", source.display(), e);
                exit(1);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate flate2;

use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::mem;
use byteorder::{LittleEndian, WriteBytesExt};
use flate2::Compression;
use flate2::write::DeflateEncoder;

struct ScenHeader<'a> {
    version: &'a[u8; 4],
    header_type: i32,
    timestamp: i32,
    instructions: &'a str,
    players: Vec<Player<'a>>,
    filename: &'a str,
    messages: ScenMessages<'a>
}

struct Player<'a> {
    name: &'a str,
    active: u32,
    human: u32,
    civilization: u32,
}

struct ScenMessages<'a> {
    objectives: &'a str,
    hints: &'a str,
    scouts: &'a str,
    history: &'a str,
    victory: &'a str,
    loss: &'a str,
}

impl<'a> ScenHeader<'a> {
    fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
        let mut buf = vec![];

        let instructions_length = self.instructions.len() as i32;
        let header_length = 20 + instructions_length;
        try!(buf.write(self.version));
        try!(buf.write_i32::<LittleEndian>(header_length));
        try!(buf.write_i32::<LittleEndian>(self.header_type));
        try!(buf.write_i32::<LittleEndian>(self.timestamp));
        try!(buf.write_i32::<LittleEndian>(instructions_length));
        try!(buf.write(self.instructions.as_bytes()));
        try!(buf.write_i32::<LittleEndian>(0));
        try!(buf.write_i32::<LittleEndian>(self.players.len() as i32));

        let mut zlib_buf = DeflateEncoder::new(buf, Compression::Default);
        try!(zlib_buf.write_u32::<LittleEndian>(0));
        try!(zlib_buf.write_f32::<LittleEndian>(1.22 /* UserPatch */));
        for i in 0..8 {
            if self.players.len() > i {
                let name = self.players[i].name;
                try!(zlib_buf.write_all(name.as_bytes()));
                try!(zlib_buf.write_all(&vec![0; 256 - name.len()]));
            } else {
                try!(zlib_buf.write_all(&vec![0; 256]));
            }
        }
        try!(zlib_buf.write_all(&vec![0; 8 * 256]));

        for i in 0..8 {
            if self.players.len() > i {
                // player name ID in string table
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            } else {
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            }
        }
        for _ in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        for i in 0..8 {
            if self.players.len() <= i {
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(4));
                continue;
            }
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].active));
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].human));
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].civilization));
            try!(zlib_buf.write_u32::<LittleEndian>(4));
        }

        // 8 * 4 times uint 0
        try!(zlib_buf.write_all(&vec![0; 8 * 4 * mem::size_of::<u32>()]));

        try!(zlib_buf.write_u32::<LittleEndian>(0));
        try!(zlib_buf.write_f32::<LittleEndian>(0.0));
        try!(zlib_buf.write_all(&[46]));
        try!(zlib_buf.write_all(self.filename.as_bytes()));
        try!(zlib_buf.write_all(
            &try!(self.messages.to_bytes())
        ));

        zlib_buf.finish()
    }
}

impl<'a> Player<'a> {
    fn empty<'b>() -> Player<'b> {
        Player { name: "", active: 0, human: 0, civilization: 0 }
    }
}

impl<'a> ScenMessages<'a> {
    fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
        let mut buf = vec![];
        for _ in 1..6 {
            try!(buf.write_i32::<LittleEndian>(0));
        }
        try!(buf.write_u16::<LittleEndian>(self.objectives.len() as u16));
        try!(buf.write(&self.objectives.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.hints.len() as u16));
        try!(buf.write(&self.hints.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.victory.len() as u16));
        try!(buf.write(&self.victory.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.loss.len() as u16));
        try!(buf.write(&self.loss.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.history.len() as u16));
        try!(buf.write(&self.history.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.scouts.len() as u16));
        try!(buf.write(&self.scouts.as_bytes()));
        Ok(buf)
    }
}

fn test(filename: &str) -> Result<(), io::Error> {
    let mut buf = try!(File::create(filename));
    let header = ScenHeader {
        version: b"1.21",
        header_type: 2,
        timestamp: 1451422223,
        instructions: "Build a fancy-pants base!",
        filename: filename,
        players: vec![
            Player { name: "William Wallace", active: 1, human: 2, civilization: 1 },
            Player { name: "Sejong the Great", active: 0, human: 2, civilization: 18 },
        ],
        messages: ScenMessages {
            objectives: "",
            hints: "",
            scouts: "",
            history: "",
            victory: "",
            loss: "",
        }
    };
    buf.write_all(&try!(header.to_bytes())).map(|_| ())
}

fn main() {
    match test("Test Scenario.scx") {
        Ok(()) => (),
        Err(e) => panic!("oops {}", e)
    }
}
<commit_msg>writing a valid scenario file \o/ 🎉<commit_after>extern crate byteorder;
extern crate flate2;

use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::mem;
use byteorder::{LittleEndian, WriteBytesExt};
use flate2::Compression;
use flate2::Flush;
use flate2::Compress;
use flate2::Status;

struct ScenHeader<'a> {
    version: &'a[u8; 4],
    header_type: i32,
    timestamp: i32,
    instructions: &'a str,
    players: Vec<Player<'a>>,
    filename: &'a str,
    messages: ScenMessages<'a>,
    image: ScenImage<'a>,
}

struct Player<'a> {
    name: &'a str,
    active: u32,
    human: u32,
    civilization: u32,
}

struct ScenMessages<'a> {
    objectives: &'a str,
    hints: &'a str,
    scouts: &'a str,
    history: &'a str,
    victory: &'a str,
    loss: &'a str,
}

struct ScenImage<'a> {
    filename: &'a str,
    included: bool,
    width: i32,
    height: i32,
    include: i16
}

impl<'a> ScenHeader<'a> {
    fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
        let mut buf = vec![];

        let instructions_length = self.instructions.len() as i32;
        let header_length = 20 + instructions_length;
        try!(buf.write(self.version));
        try!(buf.write_i32::<LittleEndian>(header_length));
        try!(buf.write_i32::<LittleEndian>(self.header_type));
        try!(buf.write_i32::<LittleEndian>(self.timestamp));
        try!(buf.write_i32::<LittleEndian>(instructions_length));
        try!(buf.write(self.instructions.as_bytes()));
        try!(buf.write_i32::<LittleEndian>(0));
        try!(buf.write_i32::<LittleEndian>(self.players.len() as i32));

        let mut zlib_buf = vec![];
        try!(zlib_buf.write_u32::<LittleEndian>(19246));
        try!(zlib_buf.write_f32::<LittleEndian>(1.22 /* UserPatch */));
        for i in 0..16 {
            if self.players.len() > i {
                let name = self.players[i].name;
                try!(zlib_buf.write_all(name.as_bytes()));
                try!(zlib_buf.write_all(&vec![0; 256 - name.len()]));
            } else {
                try!(zlib_buf.write_all(&vec![0; 256]));
            }
        }

        for i in 0..16 {
            if self.players.len() > i {
                // player name ID in string table
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            } else {
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            }
        }

        for i in 0..16 {
            if self.players.len() <= i {
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(0));
                try!(zlib_buf.write_u32::<LittleEndian>(4));
                continue;
            }
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].active));
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].human));
            try!(zlib_buf.write_u32::<LittleEndian>(self.players[i].civilization));
            try!(zlib_buf.write_u32::<LittleEndian>(4));
        }

        try!(zlib_buf.write_u32::<LittleEndian>(1));
        try!(zlib_buf.write_all(&[0]));
        try!(zlib_buf.write_f32::<LittleEndian>(-1.0));
        try!(zlib_buf.write_u16::<LittleEndian>(self.filename.len() as u16));
        try!(zlib_buf.write_all(self.filename.as_bytes()));

        try!(zlib_buf.write_all(
            &try!(self.messages.to_bytes())
        ));

        // cinematics
        try!(zlib_buf.write_u16::<LittleEndian>(0));
        try!(zlib_buf.write_u16::<LittleEndian>(0));
        try!(zlib_buf.write_u16::<LittleEndian>(0));

        try!(zlib_buf.write_all(
            &try!(self.image.to_bytes())
        ));

        for _ in 0..16 {
            // two 0-length strings
            try!(zlib_buf.write_u16::<LittleEndian>(0));
            try!(zlib_buf.write_u16::<LittleEndian>(0));
        }

        // Player AI names
        for _ in 0..8 {
            try!(zlib_buf.write_u16::<LittleEndian>(0));
        }
        // Unused players
        for _ in 0..8 {
            let name = "RandomGame";
            try!(zlib_buf.write_u16::<LittleEndian>(name.len() as u16));
            try!(zlib_buf.write(&name.as_bytes()));
        }
        // AI source code
        for _ in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
            try!(zlib_buf.write_i32::<LittleEndian>(0));
            // 0-length AI source code string
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }
        // Source code for unused players
        for _ in 0..(3 * 8) {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }

        // AI type
        for _ in 0..8 {
            try!(zlib_buf.write(&[0]));
        }
        // Unused players
        try!(zlib_buf.write(&[0; 8]));

        // Separator
        try!(zlib_buf.write_u32::<LittleEndian>(0xFFFFFF9D));

        // Resources
        for _ in 0..8 {
            try!(zlib_buf.write_u32::<LittleEndian>(100 /* gold */));
            try!(zlib_buf.write_u32::<LittleEndian>(200 /* wood */));
            try!(zlib_buf.write_u32::<LittleEndian>(200 /* food */));
            try!(zlib_buf.write_u32::<LittleEndian>(100 /* stone */));
            try!(zlib_buf.write_u32::<LittleEndian>(0 /* ore */));
            try!(zlib_buf.write_u32::<LittleEndian>(0 /* ??? */));
        }
        // Unused players
        try!(zlib_buf.write(&[0; 6 * 8 * 4]));

        // Separator
        try!(zlib_buf.write_u32::<LittleEndian>(0xFFFFFF9D));

        // Scenario goals: 10 * int32
        // Conquest; unknown; Relics; unknown; Exploration; unknown;
        // All; Mode; Score; Time Limit
        try!(zlib_buf.write(&[0; 10 * 4]));

        // Diplomacy
        for fromPlayer in 0..16 {
            for toPlayer in 0..16 {
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            }
        }

        // ???
        try!(zlib_buf.write(&[0; 11520]));

        // Separator
        try!(zlib_buf.write_u32::<LittleEndian>(0xFFFFFF9D));

        // Allied victory
        for player in 0..16 {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }

        // Technology count??
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }
        // Technology something??
        for _ in 0..(16 * 30) {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        // Unit count??
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }
        // Unit something??
        for _ in 0..(16 * 30) {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        // Building count??
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(0));
        }
        for player in 0..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }
        // Buildings something??
        for _ in 0..(16 * 20) {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        // ???
        try!(zlib_buf.write_u32::<LittleEndian>(0));
        try!(zlib_buf.write_u32::<LittleEndian>(0));
        // All Techs
        try!(zlib_buf.write_u32::<LittleEndian>(0));

        // Starting age
        for _ in 0..8 {
            try!(zlib_buf.write_u32::<LittleEndian>(0));
        }
        // Gaia
        try!(zlib_buf.write_u32::<LittleEndian>(0));
        // Unused
        for _ in 1..8 {
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        // Separator
        try!(zlib_buf.write_u32::<LittleEndian>(0xFFFFFF9D));

        // Camera
        try!(zlib_buf.write_i32::<LittleEndian>(0 /* x */));
        try!(zlib_buf.write_i32::<LittleEndian>(0 /* y */));

        // AI type
        try!(zlib_buf.write_u32::<LittleEndian>(0));

        // Map tiles
        try!(zlib_buf.write_i32::<LittleEndian>(220 /* x */));
        try!(zlib_buf.write_i32::<LittleEndian>(220 /* y */));
        for x in 0..220 {
            for y in 0..220 {
                try!(zlib_buf.write(&[
                    ((x + y) % 40) as u8, // Type
                    1, // Elevation
                    0, // ???
                ]));
            }
        }

        // Units sections
        try!(zlib_buf.write_u32::<LittleEndian>(9));

        // Resources again??
        for i in 0..8 {
            try!(zlib_buf.write_f32::<LittleEndian>(100.0 /* gold */));
            try!(zlib_buf.write_f32::<LittleEndian>(200.0 /* wood */));
            try!(zlib_buf.write_f32::<LittleEndian>(200.0 /* food */));
            try!(zlib_buf.write_f32::<LittleEndian>(100.0 /* stone */));
            try!(zlib_buf.write_f32::<LittleEndian>(0.0 /* ore */));
            try!(zlib_buf.write_f32::<LittleEndian>(0.0 /* ??? */));
            try!(zlib_buf.write_f32::<LittleEndian>(0.0 /* population */));
        }

        for i in 0..9 {
            // Zero units
            try!(zlib_buf.write_u32::<LittleEndian>(0));
            // for unit in players[i].units:
            //     putFloat(unit.x)
            //     putFloat(unit.y)
            //     putFloat(unit.unknown1)
            //     putUInt32(unit.id)
            //     putUInt16(unit.type)
            //     putInt8(unit.unknown2)
            //     putFloat(unit.angle)
            //     putUInt16(unit.frame)
            //     putInt32(unit.inId)
        }

        // Playable players
        try!(zlib_buf.write_u32::<LittleEndian>(9));

        for player in 1..9 {
            let name = "Promisory";
            try!(zlib_buf.write_i16::<LittleEndian>(name.len() as i16));
            try!(zlib_buf.write(&name.as_bytes()));
            try!(zlib_buf.write_f32::<LittleEndian>(101.0 /* camera x */));
            try!(zlib_buf.write_f32::<LittleEndian>(101.0 /* camera y */));
            try!(zlib_buf.write_i16::<LittleEndian>(101 /* ?? */));
            try!(zlib_buf.write_i16::<LittleEndian>(101 /* ?? */));
            try!(zlib_buf.write(&[0])); // allied victory (again?)
            // Diplomacy again
            try!(zlib_buf.write_u16::<LittleEndian>(9));
            try!(zlib_buf.write(&[0; 9]));
            // Diplo to gaia?? from gaia?
            for _ in 0..9 {
                try!(zlib_buf.write_i32::<LittleEndian>(0));
            }
            // Player colour
            try!(zlib_buf.write_u32::<LittleEndian>(player));
            // ???
            try!(zlib_buf.write_f32::<LittleEndian>(2.0));
            try!(zlib_buf.write_u16::<LittleEndian>(0));
            // ???
            try!(zlib_buf.write(&[0; 8 + 7]));
            try!(zlib_buf.write_i32::<LittleEndian>(-1));
        }

        try!(zlib_buf.write_u32::<LittleEndian>(2576980378));
        try!(zlib_buf.write_u32::<LittleEndian>(1073322393));
        try!(zlib_buf.write(&[0]));
        // Triggers
        try!(zlib_buf.write_i32::<LittleEndian>(0));

        try!(zlib_buf.write_u32::<LittleEndian>(0));
        try!(zlib_buf.write_u32::<LittleEndian>(0));

        let mut compressed_buf = vec![];
        compressed_buf.reserve(zlib_buf.len());
        let mut compressor = Compress::new(Compression::Default, false);
        match compressor.compress_vec(&zlib_buf, &mut compressed_buf, Flush::Sync) {
            Status::Ok => println!("hoi"),
            Status::BufError => panic!("BufError"),
            Status::StreamEnd => panic!("StreamEnd"),
        };

        println!("compressed: {} -> {}", zlib_buf.len(), compressed_buf.len());

        buf.write_all(&compressed_buf);
        Ok(buf)
    }
}

impl<'a> Player<'a> {
    fn empty<'b>() -> Player<'b> {
        Player { name: "", active: 0, human: 0, civilization: 0 }
    }
}

impl<'a> ScenMessages<'a> {
    fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
        let mut buf = vec![];
        for _ in 0..6 {
            try!(buf.write_i32::<LittleEndian>(0));
        }
        try!(buf.write_u16::<LittleEndian>(self.objectives.len() as u16));
        try!(buf.write(&self.objectives.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.hints.len() as u16));
        try!(buf.write(&self.hints.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.victory.len() as u16));
        try!(buf.write(&self.victory.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.loss.len() as u16));
        try!(buf.write(&self.loss.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.history.len() as u16));
        try!(buf.write(&self.history.as_bytes()));
        try!(buf.write_u16::<LittleEndian>(self.scouts.len() as u16));
        try!(buf.write(&self.scouts.as_bytes()));
        Ok(buf)
    }
}

impl <'a> ScenImage<'a> {
    fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
        let mut buf = vec![];
        try!(buf.write_u16::<LittleEndian>(self.filename.len() as u16));
        try!(buf.write(&self.filename.as_bytes()));
        try!(buf.write_i32::<LittleEndian>(if self.included { 1 } else { 0 }));
        try!(buf.write_i32::<LittleEndian>(self.width));
        try!(buf.write_i32::<LittleEndian>(self.height));
        try!(buf.write_i16::<LittleEndian>(self.include));
        Ok(buf)
    }
}

fn test(filename: &str) -> Result<(), io::Error> {
    let mut buf = try!(File::create(filename));
    let header = ScenHeader {
        version: b"1.21",
        header_type: 2,
        timestamp: 1451422223,
        instructions: "Build a fancy-pants base!",
        filename: filename,
        players: vec![
            Player { name: "Hello World, from Rust!", active: 1, human: 2, civilization: 1 },
            Player { name: "Filthy Opponent", active: 1, human: 0, civilization: 18 },
        ],
        messages: ScenMessages {
            objectives: "",
            hints: "",
            scouts: "",
            history: "",
            victory: "",
            loss: "",
        },
        image: ScenImage {
            filename: "",
            included: false,
            width: 0,
            height: 0,
            include: 1,
        }
    };
    buf.write_all(&try!(header.to_bytes())).map(|_| ())
}

fn main() {
    match test("Test Scenario.scx") {
        Ok(()) => (),
        Err(e) => panic!("oops {}", e)
    }
}
<|endoftext|>


<commit_before>extern crate rand;
mod zdt1;
mod gaussian_mutation;
use rand::{random, Closed01};
use zdt1::zdt1;
use gaussian_mutation::gaussian_mutation;

#[derive(Copy, Clone, Debug)]
struct Solution {
    parameters: [f32; 30],
    objectives: [f32; 2],
}

fn initialise_population(initial_population: bool) -> [Solution; 100] {
    let mut population = [Solution {
        parameters: [0_f32; 30],
        objectives: [0_f32; 2],
    }; 100];

    if initial_population {
        for solution in &mut population[..] {
            for parameter in &mut solution.parameters[..] {
                *parameter = random::<Closed01<f32>>().0;
            }

            solution.objectives = zdt1(solution.parameters);
        }
    }

    return population;
}

fn main() {
    let generations = 10;
    let mutation_rate = 0.2_f32;
    let mut parent_population = initialise_population(true);
    let mut offspring_population = initialise_population(false);

    for gen in 0..generations {
        for solution in &mut parent_population[..] {
            solution.parameters = gaussian_mutation(solution.parameters, mutation_rate);
            solution.objectives = zdt1(solution.parameters);
        }
    }
}
<commit_msg>Reduced scope of project. Program now functional<commit_after>extern crate rand;
mod zdt1;
mod gaussian_mutation;
use rand::{random, Closed01};
use zdt1::zdt1;
use gaussian_mutation::gaussian_mutation;

#[derive(Copy, Clone, Debug)]
struct Solution {
    parameters: [f32; 30],
    objectives: [f32; 2],
}

fn initialise_population(initial_population: bool) -> [Solution; 100] {
    let mut population = [Solution {
        parameters: [0_f32; 30],
        objectives: [0_f32; 2],
    }; 100];

    if initial_population {
        for solution in &mut population[..] {
            for parameter in &mut solution.parameters[..] {
                *parameter = random::<Closed01<f32>>().0;
            }

            solution.objectives = zdt1(solution.parameters);
        }
    }

    return population;
}

fn main() {
    let generations = 1000;
    let mutation_rate = 0.2_f32;
    let mut parent_population = initialise_population(true);

    for gen in 0..generations {
        for i in 0..parent_population.len() {
            let mut candidate_solution = Solution {
                parameters: parent_population[i].parameters,
                objectives: parent_population[i].objectives,
            };

            candidate_solution.parameters = gaussian_mutation(parent_population[i].parameters,
                                                              mutation_rate);
            candidate_solution.objectives = zdt1(candidate_solution.parameters);

            if dominates(candidate_solution.objectives,
                         parent_population[i].objectives) {
                parent_population[i] = candidate_solution;
            }
        }
    }

    for i in 0..parent_population.len() {
        println!("{:?}", parent_population[i].objectives);
    }
}

fn dominates(offspring_objectives: [f32; 2], parent_objectives: [f32; 2]) -> bool {
    if (offspring_objectives[0] <= parent_objectives[0]) &&
       (offspring_objectives[1] <= parent_objectives[1]) {
        return true;
    }
    return false;
}
<|endoftext|>