<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [(--write | --no-write)] [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
      --no-write         Disable write mode.
  -r <r>, --release=<r>  Create release on GitHub and publish on crates.io (only in write mode) [default: yes]
  -b <b>, --branch=<b>   The branch on which releases should happen. [default: master]
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_no_write: bool,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

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

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match (args.flag_write, args.flag_no_write) {
        (true, false) => true,
        (false, true) => false,
        _ => ci_env_set(),
    };

    let is_dry_run = !write_mode;
    let release_mode = string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if !is_dry_run && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            logger::stdout("Pushing new commit and tag");
            git::push(&config, &tag_name)
                .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

            logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
            thread::sleep(Duration::from_secs(1));

            logger::stdout("Creating GitHub release");
            github::release(&config, &tag_name, &tag_message)
                .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));

            logger::stdout("Publishing crate on crates.io");
            if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
                print_exit!("Failed to publish on crates.io");
            }

            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<commit_msg>feat: Disable release mode if write mode is disabled<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [(--write | --no-write)] [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
      --no-write         Disable write mode.
  -r <r>, --release=<r>  Create release on GitHub and publish on crates.io (only in write mode) [default: yes]
  -b <b>, --branch=<b>   The branch on which releases should happen. [default: master]
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_no_write: bool,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

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

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match (args.flag_write, args.flag_no_write) {
        (true, false) => true,
        (false, true) => false,
        _ => ci_env_set(),
    };

    let is_dry_run = !write_mode;
    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if !is_dry_run && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            logger::stdout("Pushing new commit and tag");
            git::push(&config, &tag_name)
                .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

            logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
            thread::sleep(Duration::from_secs(1));

            logger::stdout("Creating GitHub release");
            github::release(&config, &tag_name, &tag_message)
                .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));

            logger::stdout("Publishing crate on crates.io");
            if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
                print_exit!("Failed to publish on crates.io");
            }

            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize;
extern crate valico;

use iron::Iron;
use rustless::{
    Application, Api, Nesting, Versioning
};
use rustless::server::header::AccessControlAllowOrigin;
use rustless::server::status::StatusCode;
use rustc_serialize::json::ToJson;
use std::process::Command;

fn main() {

    println!("Starting RPI Management ...");

    let api = Api::build(|api| {

        api.get("ping", |endpoint| {
            endpoint.handle(|mut client, params| {
                client.set_header(AccessControlAllowOrigin::Any);
                client.empty()
            })
        });

        api.post("shutdown", |endpoint| {
            endpoint.handle(|mut client, params| {
                println!("Shutdown requested...");
                client.set_header(AccessControlAllowOrigin::Any);

                let output = Command::new("shutdown")
                    .arg("-h")
                    .arg("now")
                    .output()
                    .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

                if output.status.success() {
                    let str_stdout = String::from_utf8(output.stdout).unwrap();
                    println!("Shutdown accepted: {}", str_stdout);

                    client.set_status(StatusCode::NoContent);
                    client.empty()
                } else {
                    let str_stderr = String::from_utf8(output.stderr).unwrap();
                    println!("Shutdown rejected: {}", str_stderr);

                    client.unauthorized();
                    client.text(str_stderr)
                }
            })
        });
    });

    let app = Application::new(api);

    Iron::new(app).http("localhost:4000").unwrap();

}
<commit_msg>Add 'health' and 'system' namespaces<commit_after>extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize;
extern crate valico;

use iron::Iron;
use rustless::{
    Application, Api, Nesting, Versioning
};
use rustless::server::header::AccessControlAllowOrigin;
use rustless::server::status::StatusCode;
use rustc_serialize::json::ToJson;
use std::process::Command;

fn main() {

    println!("Starting RPI Management ...");

    let api = Api::build(|api| {

        api.namespace("health", |health_ns| {

            health_ns.get("ping", |endpoint| {
                endpoint.handle(|mut client, params| {
                    client.set_header(AccessControlAllowOrigin::Any);
                    client.empty()
                })
            });

        });

        api.namespace("system", |system_ns| {

            system_ns.post("shutdown", |endpoint| {
                endpoint.handle(|mut client, params| {
                    println!("Shutdown requested...");
                    client.set_header(AccessControlAllowOrigin::Any);

                    let output = Command::new("shutdown")
                        .arg("-h")
                        .arg("now")
                        .output()
                        .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

                    if output.status.success() {
                        let str_stdout = String::from_utf8(output.stdout).unwrap();
                        println!("Shutdown accepted: {}", str_stdout);

                        client.set_status(StatusCode::NoContent);
                        client.empty()
                    } else {
                        let str_stderr = String::from_utf8(output.stderr).unwrap();
                        println!("Shutdown rejected: {}", str_stderr);

                        client.unauthorized();
                        client.text(str_stderr)
                    }
                })
            });

        });

    });

    let app = Application::new(api);

    Iron::new(app).http("localhost:4000").unwrap();

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::BTreeMap;

use std::env;
use std::fs::File;
use std::io::prelude::*;

macro_rules! die(
    ($($arg:tt)*) => { {
        writeln!(std::io::stderr(), $($arg)*)
            .expect("Failed to print to stderr");
        std::process::exit(1);
    } }
);

#[derive(Deserialize)]
struct Config {
    commands: BTreeMap<String,String>,
}

// Read the config file into a Config struct
fn read_config<P: AsRef<std::path::Path>>(path: P) -> Result<Config, Box<std::error::Error>> {
    let mut contents = String::new();

    File::open(path)?
        .read_to_string(&mut contents)?;

    let config: Config = toml::from_str(&contents)?;
    Ok(config)
}

fn run_command(command: &str) -> Result<i32, Box<std::error::Error>> {
    let mut child = std::process::Command::new("/bin/sh")
        .arg("-c")
        .arg(command)
        .spawn()?;

    child
        .wait()?
        .code()
        .ok_or(Box::new(std::io::Error::last_os_error()))
}

fn main() {
    let program_name = std::env::args().nth(0).unwrap_or("resh".to_string());

    let command_alias = match std::env::args().nth(1) {
        Some(alias) => alias,
        None => { die!("Usage: {} <command alias>", program_name) }
    };

    let config_file = env::var("RESH_CONFIG")
        .unwrap_or_else(|_| {"/etc/resh.yml".to_string()});

    let config: Config = read_config(&config_file).
        unwrap_or_else(|e| { die!("Failed to read {}: {}", config_file, e); });

    let full_command = match config.commands.get(&command_alias) {
        Some(cmd) => cmd,
        None => { die!("Undefined command alias: {}", command_alias) },
    };

    let exitcode = run_command(&full_command)
        .unwrap_or(1);

    std::process::exit(exitcode);
}
<commit_msg>Fix default config file extension<commit_after>#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::BTreeMap;

use std::env;
use std::fs::File;
use std::io::prelude::*;

macro_rules! die(
    ($($arg:tt)*) => { {
        writeln!(std::io::stderr(), $($arg)*)
            .expect("Failed to print to stderr");
        std::process::exit(1);
    } }
);

#[derive(Deserialize)]
struct Config {
    commands: BTreeMap<String,String>,
}

fn read_config<P: AsRef<std::path::Path>>(path: P) -> Result<Config, Box<std::error::Error>> {
    let mut contents = String::new();

    File::open(path)?
        .read_to_string(&mut contents)?;

    let config: Config = toml::from_str(&contents)?;
    Ok(config)
}

fn run_command(command: &str) -> Result<i32, Box<std::error::Error>> {
    let mut child = std::process::Command::new("/bin/sh")
        .arg("-c")
        .arg(command)
        .spawn()?;

    child
        .wait()?
        .code()
        .ok_or(Box::new(std::io::Error::last_os_error()))
}

fn main() {
    let program_name = std::env::args().nth(0).unwrap_or("resh".to_string());

    let command_alias = match std::env::args().nth(1) {
        Some(alias) => alias,
        None => { die!("Usage: {} <command alias>", program_name) }
    };

    let config_file = env::var("RESH_CONFIG")
        .unwrap_or_else(|_| {"/etc/resh.toml".to_string()});

    let config: Config = read_config(&config_file).
        unwrap_or_else(|e| { die!("Failed to read {}: {}", config_file, e); });

    let full_command = match config.commands.get(&command_alias) {
        Some(cmd) => cmd,
        None => { die!("Undefined command alias: {}", command_alias) },
    };

    let exitcode = run_command(&full_command)
        .unwrap_or(1);

    std::process::exit(exitcode);
}
<|endoftext|>


<commit_before>extern crate actix;
extern crate actix_web;
extern crate env_logger;
#[macro_use]
extern crate rust_embed;
extern crate futures;
extern crate json;
extern crate mime_guess;
extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rusqlite;
extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate time;

use self::actix::prelude::*;
use actix_web::http::{header, Method};
use actix_web::{server, App, Body, HttpRequest, HttpResponse};
use futures::future::Future;
use mime_guess::guess_mime_type;
use r2d2_sqlite::SqliteConnectionManager;
use std::sync::Arc;

mod api;
mod db;
mod geoip;
use self::db::{DbExecutor, LogVisit};
use self::geoip::{GeoIPExecutor, LookupIP};

#[derive(RustEmbed)]
#[folder = "frontend/out/"]
struct Asset;

fn handle_embedded_file(path: &str) -> HttpResponse {
    let canonical_path = if path.is_empty() {
        "index.html"
    } else if path == "resume" || path == "resume/" {
        "resume/index.html"
    } else {
        path
    };
    match Asset::get(canonical_path) {
        Some(content) => HttpResponse::Ok()
            .header(
                header::CONTENT_TYPE,
                guess_mime_type(canonical_path).as_ref(),
            )
            .body(Body::from_slice(content.as_ref())),
        None => HttpResponse::NotFound().body("404 Not Found"),
    }
}

struct State {
    db: Addr<DbExecutor>,
    geoip: Addr<GeoIPExecutor>,
}

fn handle_static(req: HttpRequest<Arc<State>>) -> HttpResponse {
    handle_embedded_file(&req.path()[1..])
}

fn handle_resume(req: HttpRequest<Arc<State>>) -> HttpResponse {
    use std::net::{AddrParseError, SocketAddr};

    let timestamp = time::now().to_timespec();
    let state = req.state().clone();
    match req.connection_info().remote() {
        Some(remote_host) => {
            let remote: Result<SocketAddr, AddrParseError> = remote_host.parse();
            match remote {
                Ok(remote) => {
                    let ip: String = remote.ip().to_string();
                    let ft = state
                        .geoip
                        .send(LookupIP {
                            address: ip.clone(),
                        })
                        .and_then(move |res| {
                            let msg = match res {
                                Ok(ref location) => LogVisit {
                                    ipaddr: ip,
                                    time: timestamp,
                                    location: location.location,
                                },

                                Err(_) => LogVisit {
                                    ipaddr: ip,
                                    time: timestamp,
                                    location: (0.0, 0.0),
                                },
                            };
                            state.db.send(msg)
                        })
                        .map(|_| ())
                        .map_err(|_| ());
                    req.server_settings().cpu_pool().spawn(ft).forget();
                }
                _ => {}
            };
        }
        _ => {
            println!("no remote");
        }
    };

    handle_embedded_file("static/resume.pdf")
}

fn main() {
    ::std::env::set_var("RUST_LOG", "actix_web=debug");

    let geoip_api_key = api::GEOIP_KEY.to_string();

    let sys = actix::System::new("website");
    let manager = SqliteConnectionManager::file("resume-logs.sqlite");
    let pool = r2d2::Pool::new(manager).unwrap();
    let db_addr = SyncArbiter::start(1, move || DbExecutor::new(pool.clone()));
    let geoip_addr = Arbiter::start(|_| GeoIPExecutor::new(geoip_api_key));

    server::new(move || {
        App::with_state(Arc::new(State {
            db: db_addr.clone(),
            geoip: geoip_addr.clone(),
        }))
        .route("/resume.pdf", Method::GET, handle_resume)
        .route("/{_:.*}", Method::GET, handle_static)
    })
    .bind("0.0.0.0:8000")
    .unwrap()
    .run();

    let _ = sys.run();
}
<commit_msg>Fix IP parsing<commit_after>extern crate actix;
extern crate actix_web;
extern crate env_logger;
#[macro_use]
extern crate rust_embed;
extern crate futures;
extern crate json;
extern crate mime_guess;
extern crate r2d2;
extern crate r2d2_sqlite;
extern crate rusqlite;
extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate time;

use self::actix::prelude::*;
use actix_web::http::{header, Method};
use actix_web::{server, App, Body, HttpRequest, HttpResponse};
use futures::future::Future;
use mime_guess::guess_mime_type;
use r2d2_sqlite::SqliteConnectionManager;
use std::sync::Arc;

mod api;
mod db;
mod geoip;
use self::db::{DbExecutor, LogVisit};
use self::geoip::{GeoIPExecutor, LookupIP};

#[derive(RustEmbed)]
#[folder = "frontend/out/"]
struct Asset;

fn handle_embedded_file(path: &str) -> HttpResponse {
    let canonical_path = if path.is_empty() {
        "index.html"
    } else if path == "resume" || path == "resume/" {
        "resume/index.html"
    } else {
        path
    };
    match Asset::get(canonical_path) {
        Some(content) => HttpResponse::Ok()
            .header(
                header::CONTENT_TYPE,
                guess_mime_type(canonical_path).as_ref(),
            )
            .body(Body::from_slice(content.as_ref())),
        None => HttpResponse::NotFound().body("404 Not Found"),
    }
}

struct State {
    db: Addr<DbExecutor>,
    geoip: Addr<GeoIPExecutor>,
}

fn handle_static(req: HttpRequest<Arc<State>>) -> HttpResponse {
    handle_embedded_file(&req.path()[1..])
}

fn handle_resume(req: HttpRequest<Arc<State>>) -> HttpResponse {
    use std::net::{AddrParseError, IpAddr, SocketAddr};

    let timestamp = time::now().to_timespec();
    let state = req.state().clone();
    match req.connection_info().remote() {
        Some(remote_host) => {
            let ip: Option<IpAddr> = {
                let socket_remote: Result<SocketAddr, AddrParseError> = remote_host.parse();
                let ipaddr_remote: Result<IpAddr, AddrParseError> = remote_host.parse();
                match socket_remote {
                    Ok(socket_remote) => { Some(socket_remote.ip()) }
                    _ => ipaddr_remote.ok()
                }
            };

            match ip {
                Some(ip) => {
                    let ip: String = ip.to_string();
                    let ft = state
                        .geoip
                        .send(LookupIP {
                            address: ip.clone(),
                        })
                        .and_then(move |res| {
                            let msg = match res {
                                Ok(ref location) => LogVisit {
                                    ipaddr: ip,
                                    time: timestamp,
                                    location: location.location,
                                },

                                Err(_) => LogVisit {
                                    ipaddr: ip,
                                    time: timestamp,
                                    location: (0.0, 0.0),
                                },
                            };
                            state.db.send(msg)
                        })
                        .map(|_| ())
                        .map_err(|_| ());
                    req.server_settings().cpu_pool().spawn(ft).forget();
                }
                _ => { println!("Failed to parse {}", remote_host); }
            };
        }
        _ => {
            println!("no remote");
        }
    };

    handle_embedded_file("static/resume.pdf")
}

fn main() {
    ::std::env::set_var("RUST_LOG", "actix_web=debug");

    let geoip_api_key = api::GEOIP_KEY.to_string();

    let sys = actix::System::new("website");
    let manager = SqliteConnectionManager::file("resume-logs.sqlite");
    let pool = r2d2::Pool::new(manager).unwrap();
    let db_addr = SyncArbiter::start(1, move || DbExecutor::new(pool.clone()));
    let geoip_addr = Arbiter::start(|_| GeoIPExecutor::new(geoip_api_key));

    server::new(move || {
        App::with_state(Arc::new(State {
            db: db_addr.clone(),
            geoip: geoip_addr.clone(),
        }))
        .route("/resume.pdf", Method::GET, handle_resume)
        .route("/{_:.*}", Method::GET, handle_static)
    })
    .bind("0.0.0.0:8000")
    .unwrap()
    .run();

    let _ = sys.run();
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

mod git;

extern crate getopts;
extern crate hyper;
extern crate oauth2;
extern crate queryst;
extern crate rustc_serialize;
extern crate tempdir;
extern crate unicase;
extern crate url;
extern crate uuid;

use getopts::Options;
use hyper::header;
use hyper::net::Openssl;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use oauth2::Config;
use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};
use queryst::parse;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::io::Read;
use std::string::String;
use std::sync::{Arc, Mutex};
use unicase::UniCase;
use url::parse_path;
use self::uuid::Uuid;

fn main() {
    let (listen_address, ssl, oauth_config, oauth_redirect_path) = parse_flags();

    let handler = Handler{
        oauth_config: oauth_config,
        oauth_redirect_path: oauth_redirect_path,
        redirect_uris: Arc::new(Mutex::new(HashMap::new())),
    };

    println!("Listening on {}", listen_address);
    if ssl.is_some() {
        Server::https(&listen_address[..], ssl.unwrap()).unwrap().handle(handler).unwrap();
    } else {
        Server::http(&listen_address[..]).unwrap().handle(handler).unwrap();
    }
}

fn parse_flags() -> (String, Option<Openssl>, oauth2::Config, String) {
    let mut opts = Options::new();

    opts.optopt("p", "port", "Port on which to listen", "3000");
    opts.optopt("i", "interface", "Interface on which to listen", "0.0.0.0");

    opts.optflag("s", "ssl", "Whether to listen for HTTPS or just HTTP");
    opts.optopt("c", "certfile", "SSL certificate file", "/path/to/foo.crt");
    opts.optopt("k", "keyfile", "SSL key file", "/path/to/foo.key");

    opts.optopt("h", "host_and_port", "Hostname and port", "127.0.0.1:443");
    opts.optopt("", "oauth_client_id", "Github oauth client ID", "clientid");
    opts.optopt("", "oauth_client_secret", "Github oauth client secret", "clientsecret");
    opts.optopt("", "oauth_redirect_path", "Github oauth redirect URL", "/oauth/redirect");

    let args: Vec<String> = env::args().collect();
    let matches = opts.parse(&args[1..]).unwrap();
    let interface = matches.opt_str("i").unwrap_or("0.0.0.0".to_owned());
    let port = matches.opt_str("p").unwrap_or("3000".to_owned());

    let ssl = match matches.opt_present("s") {
        true => Some(Openssl::with_cert_and_key(matches.opt_str("c").unwrap(), matches.opt_str("k").unwrap()).unwrap()),
        false => None,
    };

    let mut oauth_config = oauth2::Config::new(
        &matches.opt_str("oauth_client_id").unwrap(),
        &matches.opt_str("oauth_client_secret").unwrap(),
        "https://github.com/login/oauth/authorize",
        "https://github.com/login/oauth/access_token"
    );
    oauth_config.scopes.push("repo".to_owned());
    let oauth_redirect_path = matches.opt_str("oauth_redirect_path").unwrap();
    oauth_config.redirect_url = format!("https://{}{}", matches.opt_str("host_and_port").unwrap(), oauth_redirect_path);

    let host_and_port = format!("{}:{}", interface, port);
    return (host_and_port, ssl, oauth_config, oauth_redirect_path);
}

struct Handler {
    oauth_config: oauth2::Config,
    oauth_redirect_path: String,
    redirect_uris: Arc<Mutex<HashMap<String, String>>>,
}

impl hyper::server::Handler for Handler {
    fn handle(&self, req: Request, mut res: Response) {
        let (path, qs) = match req.uri {
            AbsolutePath(ref p) => match parse_path(p) {
                Ok((path, qs, _)) => (format!("/{}", path.join("/")), qs),
                Err(err) => { self.error(res, err.description()); return },
            },
            _ => { self.not_found(res); return },
        };

        res.headers_mut().set(header::AccessControlAllowOrigin::Any);
        res.headers_mut().set(header::AccessControlAllowHeaders(vec![UniCase("content-type".to_owned())]));

        if req.method == hyper::method::Method::Options {
            return
        }

        match &path[..] {
            "/squashmerge" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_squash_request) },
                _ => self.not_allowed(res)
            },
            "/rewritehistory" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_rewrite_history_request) },
                _ => self.not_allowed(res),
            },
            "/oauth/authorize" => match req.method {
                hyper::Get => self.handle_oauth_authorize_request(qs, res),
                _ => self.not_allowed(res),
            },
            _ => {
                if path == &self.oauth_redirect_path[..] {
                    match req.method {
                        hyper::Get => match qs {
                            Some(q) => self.handle_oauth_exchange_request(&q, res),
                            None => self.bad_request(res),
                        },
                        _ => self.not_allowed(res),
                    }
                } else {
                    self.not_found(res);
                }
            },
        };
    }
}

impl Handler {
    fn handle_request<F>(&self, mut req: Request, res: Response, handler: F)
        where F : Fn(&str) -> Result<String, String> {

        let mut body_string = String::new();
        let err = req.read_to_string(&mut body_string);
        if err.is_err() {
            self.error(res, &err.err().unwrap().description());
            return;
        }

        match handler(&body_string) {
            Ok(sha) => self.send_json(res, &ShaResponse { sha: &sha, merged: true }),
            Err(err) => self.error(res, &err),
        };
    }

    fn handle_squash_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::squash_merge(
            get_json_string!(body, "base_repo"),
            get_json_string!(body, "base_branch"),
            get_json_string!(body, "head_repo"),
            get_json_string!(body, "head_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password"),
        );
    }

    fn handle_rewrite_history_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::rewrite_history(
            get_json_string!(body, "repo"),
            get_json_string!(body, "branch"),
            get_json_string!(body, "baseline_repo"),
            get_json_string!(body, "baseline_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password")
        );
    }

    fn handle_oauth_authorize_request(&self, qs: Option<String>, mut res: Response) {
        let uuid = Uuid::new_v4().to_hyphenated_string();

        let redirect_uri = self.get_redirect_uri(qs);

        if redirect_uri.is_some() {
            let mut uris = self.redirect_uris.lock().unwrap();
            uris.insert(uuid.clone(), redirect_uri.unwrap().to_owned());
        }

        let _ = *res.status_mut() = StatusCode::Found;
        res.headers_mut().set(header::Location(self.oauth_config.authorize_url(uuid).to_string()));
    }

    fn handle_oauth_exchange_request(&self, qs: &str, res: Response) {
        let obj = match parse(qs) {
            Ok(q) => q,
            Err(err) => { self.error(res, &format!("Error parsing querystring: {}", err.message)); return },
        };
        let code = match obj.find("code") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "code not a string"); return },
            },
            None => { self.error(res, "No code present"); return },
        };
        let state = match obj.find("state") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "state not a string"); return },
            },
            None => { self.error(res, "No state present"); return },
        };
        let token = self.oauth_config.exchange(code.to_owned());
        match token {
            Ok(tok) => self.respond_with_token(res, state, &tok.access_token),
            Err(tok) => self.error(res, &format!("Error exchanging token: {}", tok)),
        };
    }

    fn respond_with_token(&self, mut res: Response, uuid: &str, token: &str) {
        let mut uris = self.redirect_uris.lock().unwrap();
        match uris.remove(uuid) {
            Some(uri) => {
                let _ = *res.status_mut() = StatusCode::Found;
                // TODO: Form URI better
                res.headers_mut().set(header::Location(format!("{}?token={}", uri, token)));
            },
            None => { self.send_json(res, &TokenResponse { access_token: &token }); }
        };
    }

    fn send_json<T: Encodable>(&self, mut res: Response, obj: &T) {
        match json::encode(obj) {
            Ok(o) => { let _ = res.send(o.as_bytes()); },
            Err(_) => {
                let _ = *res.status_mut() = StatusCode::InternalServerError;
                let _ = res.send("{\"message\": \"error serializing response\"}".as_bytes());
            },
        };
    }

    fn get_redirect_uri(&self, qs: Option<String>) -> Option<String> {
        let kv = match qs {
            Some(q) => match parse(&q) {
                Ok(kv) => kv,
                Err(_) => return None,
            },
            None => return None,
        };
        return kv.find("redirect_uri")
            .and_then(|v| v.as_string())
            .and_then(|v| Some(v.to_owned().clone()));
    }

    fn error(&self, mut res: Response, description: &str) {
        let _ = *res.status_mut() = StatusCode::InternalServerError;
        self.send_json(res, &ErrorResponse { message: description });
    }

    fn bad_request(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::BadRequest;
    }

    fn not_allowed(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::MethodNotAllowed;
    }

    fn not_found(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::NotFound;
    }
}

#[derive(RustcEncodable)]
struct ShaResponse<'a> {
    sha: &'a str,
    merged: bool,
}

#[derive(RustcEncodable)]
struct ErrorResponse<'a> {
    message: &'a str,
}

#[derive(RustcEncodable)]
struct TokenResponse<'a> {
    access_token: &'a str,
}
<commit_msg>Take oauth scopes as querystring params<commit_after>#[macro_use]
mod macros;

mod git;

extern crate getopts;
extern crate hyper;
extern crate oauth2;
extern crate queryst;
extern crate rustc_serialize;
extern crate tempdir;
extern crate unicase;
extern crate url;
extern crate uuid;

use getopts::Options;
use hyper::header;
use hyper::net::Openssl;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use oauth2::Config;
use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};
use queryst::parse;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::io::Read;
use std::string::String;
use std::sync::{Arc, Mutex};
use unicase::UniCase;
use url::parse_path;
use self::uuid::Uuid;

fn main() {
    let (listen_address, ssl, oauth_config, oauth_redirect_path) = parse_flags();

    let handler = Handler{
        oauth_config: oauth_config,
        oauth_redirect_path: oauth_redirect_path,
        redirect_uris: Arc::new(Mutex::new(HashMap::new())),
    };

    println!("Listening on {}", listen_address);
    if ssl.is_some() {
        Server::https(&listen_address[..], ssl.unwrap()).unwrap().handle(handler).unwrap();
    } else {
        Server::http(&listen_address[..]).unwrap().handle(handler).unwrap();
    }
}

fn parse_flags() -> (String, Option<Openssl>, oauth2::Config, String) {
    let mut opts = Options::new();

    opts.optopt("p", "port", "Port on which to listen", "3000");
    opts.optopt("i", "interface", "Interface on which to listen", "0.0.0.0");

    opts.optflag("s", "ssl", "Whether to listen for HTTPS or just HTTP");
    opts.optopt("c", "certfile", "SSL certificate file", "/path/to/foo.crt");
    opts.optopt("k", "keyfile", "SSL key file", "/path/to/foo.key");

    opts.optopt("h", "host_and_port", "Hostname and port", "127.0.0.1:443");
    opts.optopt("", "oauth_client_id", "Github oauth client ID", "clientid");
    opts.optopt("", "oauth_client_secret", "Github oauth client secret", "clientsecret");
    opts.optopt("", "oauth_redirect_path", "Github oauth redirect URL", "/oauth/redirect");

    let args: Vec<String> = env::args().collect();
    let matches = opts.parse(&args[1..]).unwrap();
    let interface = matches.opt_str("i").unwrap_or("0.0.0.0".to_owned());
    let port = matches.opt_str("p").unwrap_or("3000".to_owned());

    let ssl = match matches.opt_present("s") {
        true => Some(Openssl::with_cert_and_key(matches.opt_str("c").unwrap(), matches.opt_str("k").unwrap()).unwrap()),
        false => None,
    };

    let mut oauth_config = oauth2::Config::new(
        &matches.opt_str("oauth_client_id").unwrap(),
        &matches.opt_str("oauth_client_secret").unwrap(),
        "https://github.com/login/oauth/authorize",
        "https://github.com/login/oauth/access_token"
    );
    let oauth_redirect_path = matches.opt_str("oauth_redirect_path").unwrap();
    oauth_config.redirect_url = format!("https://{}{}", matches.opt_str("host_and_port").unwrap(), oauth_redirect_path);

    let host_and_port = format!("{}:{}", interface, port);
    return (host_and_port, ssl, oauth_config, oauth_redirect_path);
}

struct Handler {
    oauth_config: oauth2::Config,
    oauth_redirect_path: String,
    redirect_uris: Arc<Mutex<HashMap<String, String>>>,
}

impl hyper::server::Handler for Handler {
    fn handle(&self, req: Request, mut res: Response) {
        let (path, qs) = match req.uri {
            AbsolutePath(ref p) => match parse_path(p) {
                Ok((path, qs, _)) => (format!("/{}", path.join("/")), qs),
                Err(err) => { self.error(res, err.description()); return },
            },
            _ => { self.not_found(res); return },
        };

        res.headers_mut().set(header::AccessControlAllowOrigin::Any);
        res.headers_mut().set(header::AccessControlAllowHeaders(vec![UniCase("content-type".to_owned())]));

        if req.method == hyper::method::Method::Options {
            return
        }

        match &path[..] {
            "/squashmerge" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_squash_request) },
                _ => self.not_allowed(res)
            },
            "/rewritehistory" => match req.method {
                hyper::Post => { self.handle_request(req, res, Handler::handle_rewrite_history_request) },
                _ => self.not_allowed(res),
            },
            "/oauth/authorize" => match req.method {
                hyper::Get => self.handle_oauth_authorize_request(qs, res),
                _ => self.not_allowed(res),
            },
            _ => {
                if path == &self.oauth_redirect_path[..] {
                    match req.method {
                        hyper::Get => match qs {
                            Some(q) => self.handle_oauth_exchange_request(&q, res),
                            None => self.bad_request(res),
                        },
                        _ => self.not_allowed(res),
                    }
                } else {
                    self.not_found(res);
                }
            },
        };
    }
}

impl Handler {
    fn handle_request<F>(&self, mut req: Request, res: Response, handler: F)
        where F : Fn(&str) -> Result<String, String> {

        let mut body_string = String::new();
        let err = req.read_to_string(&mut body_string);
        if err.is_err() {
            self.error(res, &err.err().unwrap().description());
            return;
        }

        match handler(&body_string) {
            Ok(sha) => self.send_json(res, &ShaResponse { sha: &sha, merged: true }),
            Err(err) => self.error(res, &err),
        };
    }

    fn handle_squash_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::squash_merge(
            get_json_string!(body, "base_repo"),
            get_json_string!(body, "base_branch"),
            get_json_string!(body, "head_repo"),
            get_json_string!(body, "head_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password"),
        );
    }

    fn handle_rewrite_history_request(body_string: &str) -> Result<String, String> {
        let body = try_or_string!(Json::from_str(body_string));

        return git::rewrite_history(
            get_json_string!(body, "repo"),
            get_json_string!(body, "branch"),
            get_json_string!(body, "baseline_repo"),
            get_json_string!(body, "baseline_branch"),
            get_json_string!(body, "commit_message"),
            get_json_string!(body, "username"),
            get_json_string!(body, "password")
        );
    }

    fn handle_oauth_authorize_request(&self, qs: Option<String>, mut res: Response) {
        let uuid = Uuid::new_v4().to_hyphenated_string();

        let redirect_uri = self.get_redirect_uri(&qs);

        if redirect_uri.is_some() {
            let mut uris = self.redirect_uris.lock().unwrap();
            uris.insert(uuid.clone(), redirect_uri.unwrap().to_owned());
        }

        let mut oauth_config = oauth2::Config::new(
            &self.oauth_config.client_id,
            &self.oauth_config.client_secret,
            &self.oauth_config.auth_url.to_string(),
            &self.oauth_config.token_url.to_string(),
        );
        oauth_config.scopes.extend(self.get_scopes(&qs));

        let _ = *res.status_mut() = StatusCode::Found;
        res.headers_mut().set(header::Location(oauth_config.authorize_url(uuid).to_string()));
    }

    fn handle_oauth_exchange_request(&self, qs: &str, res: Response) {
        let obj = match parse(qs) {
            Ok(q) => q,
            Err(err) => { self.error(res, &format!("Error parsing querystring: {}", err.message)); return },
        };
        let code = match obj.find("code") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "code not a string"); return },
            },
            None => { self.error(res, "No code present"); return },
        };
        let state = match obj.find("state") {
            Some(c) => match c.as_string() {
                Some(c) => c,
                None => { self.error(res, "state not a string"); return },
            },
            None => { self.error(res, "No state present"); return },
        };
        let token = self.oauth_config.exchange(code.to_owned());
        match token {
            Ok(tok) => self.respond_with_token(res, state, &tok.access_token),
            Err(tok) => self.error(res, &format!("Error exchanging token: {}", tok)),
        };
    }

    fn respond_with_token(&self, mut res: Response, uuid: &str, token: &str) {
        let mut uris = self.redirect_uris.lock().unwrap();
        match uris.remove(uuid) {
            Some(uri) => {
                let _ = *res.status_mut() = StatusCode::Found;
                // TODO: Form URI better
                res.headers_mut().set(header::Location(format!("{}?token={}", uri, token)));
            },
            None => { self.send_json(res, &TokenResponse { access_token: &token }); }
        };
    }

    fn send_json<T: Encodable>(&self, mut res: Response, obj: &T) {
        match json::encode(obj) {
            Ok(o) => { let _ = res.send(o.as_bytes()); },
            Err(_) => {
                let _ = *res.status_mut() = StatusCode::InternalServerError;
                let _ = res.send("{\"message\": \"error serializing response\"}".as_bytes());
            },
        };
    }

    fn get_redirect_uri(&self, qs: &Option<String>) -> Option<String> {
        return self.get_string_qs(qs, "redirect_uri");
    }

    fn get_scopes(&self, qs: &Option<String>) -> Vec<String> {
        let val = self.get_string_qs(qs, "scopes")
            .clone()
            .unwrap_or("".to_owned());
        return val
            .split(",")
            .map(|x| x.to_string())
            .collect();
    }

    fn get_string_qs(&self, qs: &Option<String>, key: &str) -> Option<String> {
        let kv = match qs.clone() {
            Some(q) => match parse(&q) {
                Ok(kv) => kv,
                Err(_) => return None,
            },
            None => return None,
        };
        return kv.find(key)
            .and_then(|v| v.as_string())
            .and_then(|v| Some(v.to_owned().clone()));
    }

    fn error(&self, mut res: Response, description: &str) {
        let _ = *res.status_mut() = StatusCode::InternalServerError;
        self.send_json(res, &ErrorResponse { message: description });
    }

    fn bad_request(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::BadRequest;
    }

    fn not_allowed(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::MethodNotAllowed;
    }

    fn not_found(&self, mut res: Response) {
        let _ = *res.status_mut() = StatusCode::NotFound;
    }
}

#[derive(RustcEncodable)]
struct ShaResponse<'a> {
    sha: &'a str,
    merged: bool,
}

#[derive(RustcEncodable)]
struct ErrorResponse<'a> {
    message: &'a str,
}

#[derive(RustcEncodable)]
struct TokenResponse<'a> {
    access_token: &'a str,
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate console;
extern crate indicatif;

use clap::{App, Arg, SubCommand};
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::fs::remove_file;
use std::process::Command;

fn main() {
    let matches = App::new("r2")
        .version(crate_version!())
        .author("Olivia Hugger")
        .about("Test")
        .subcommand(SubCommand::with_name("verify").alias("v"))
        .subcommand(
            SubCommand::with_name("run")
                .alias("r")
                .arg(Arg::with_name("file").required(true).index(1)),
        ).get_matches();

    println!(r#"                 _   _ _                  "#);
    println!(r#"  _ __ _   _ ___| |_| (_)_ __   __ _ ___  "#);
    println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
    println!(r#" | |  | |_| \__ \ |_| | | | | | (_| \__ \ "#);
    println!(r#" |_|   \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
    println!(r#"                               |___/      "#);
    println!("");

    if let Some(matches) = matches.subcommand_matches("run") {
        if let Some(filename) = matches.value_of("file") {
            let bar = ProgressBar::new_spinner();
            bar.set_message(format!("Compiling {}...", filename).as_str());
            bar.enable_steady_tick(100);
            let compilecmd = Command::new("rustc")
                .args(&[filename, "-o", "temp"])
                .output()
                .expect("fail");
            bar.set_message(format!("Running {}...", filename).as_str());
            if compilecmd.status.success() {
                let runcmd = Command::new("./temp").output().expect("fail");
                bar.finish_and_clear();

                if runcmd.status.success() {
                    println!("{}", String::from_utf8_lossy(&runcmd.stdout));
                    let formatstr =
                        format!("{} Successfully ran {}", Emoji("✅", "✓"), filename);
                    println!("{}", style(formatstr).green());
                    clean().unwrap();
                } else {
                    println!("{}", String::from_utf8_lossy(&runcmd.stdout));
                    println!("{}", String::from_utf8_lossy(&runcmd.stderr));

                    let formatstr =
                        format!("{} Ran {} with errors", Emoji("⚠ ", "!"), filename);
                    println!("{}", style(formatstr).red());
                    clean().unwrap();
                }
            } else {
                bar.finish_and_clear();
                let formatstr = format!(
                    "{} Compilation of {} failed! Compiler error message:\n",
                    Emoji("⚠ ", "!"),
                    filename
                );
                println!("{}", style(formatstr).red());
                println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
                clean().unwrap();
            }
        } else {
            panic!("Please supply a filename!");
        }
    }

    if let Some(_) = matches.subcommand_matches("verify") {
        compile_only("exercises/ex1.rs");
        compile_only("exercises/ex2.rs");
        compile_only("exercises/ex3.rs");
        compile_only("exercises/ex4.rs");
        compile_only("exercises/ex5.rs");

        compile_only("exercises/variables/variables1.rs");
        compile_only("exercises/variables/variables2.rs");
        compile_only("exercises/variables/variables3.rs");
        compile_only("exercises/variables/variables4.rs");

        compile_only("exercises/functions/functions1.rs");
        compile_only("exercises/functions/functions2.rs");
        compile_only("exercises/functions/functions3.rs");
        compile_only("exercises/functions/functions4.rs");
        compile_only("exercises/functions/functions5.rs");

        compile_only("exercises/primitive_types/primitive_types1.rs");
        compile_only("exercises/primitive_types/primitive_types2.rs");
        compile_only("exercises/primitive_types/primitive_types3.rs");
        compile_only("exercises/primitive_types/primitive_types4.rs");
        compile_only("exercises/primitive_types/primitive_types5.rs");
        compile_only("exercises/primitive_types/primitive_types6.rs");

        test("exercises/tests/tests1.rs");
        test("exercises/tests/tests2.rs");
        test("exercises/tests/tests3.rs");
        test("exercises/tests/tests4.rs");

        test("exercises/if/if1.rs");

        compile_only("exercises/strings/strings1.rs");
        compile_only("exercises/strings/strings2.rs");
        compile_only("exercises/strings/strings3.rs");

        compile_only("exercises/move_semantics/move_semantics1.rs");
        compile_only("exercises/move_semantics/move_semantics2.rs");
        compile_only("exercises/move_semantics/move_semantics3.rs");
        compile_only("exercises/move_semantics/move_semantics4.rs");

        compile_only("exercises/modules/modules1.rs");
        compile_only("exercises/modules/modules2.rs");

        compile_only("exercises/macros/macros1.rs");
        compile_only("exercises/macros/macros2.rs");
        compile_only("exercises/macros/macros3.rs");
        compile_only("exercises/macros/macros4.rs");

        test("exercises/error_handling/errors1.rs");
        test("exercises/error_handling/errors2.rs");
        test("exercises/error_handling/errors3.rs");
        test("exercises/error_handling/errorsn.rs");
        compile_only("exercises/error_handling/option1.rs");
        test("exercises/error_handling/result1.rs");
    }
}

fn compile_only(filename: &str) {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Compiling {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let compilecmd = Command::new("rustc")
        .args(&[filename, "-o", "temp"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if compilecmd.status.success() {
        let formatstr = format!(
            "{} Successfully compiled {}!",
            Emoji("✅", "✓"),
            filename
        );
        println!("{}", style(formatstr).green());
        clean().unwrap();
    } else {
        let formatstr = format!(
            "{} Compilation of {} failed! Compiler error message:\n",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
        clean().unwrap();
        std::process::exit(1);
    }
}

fn test(filename: &str) {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Testing {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let testcmd = Command::new("rustc")
        .args(&["--test", filename, "-o", "temp"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if testcmd.status.success() {
        let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename);
        println!("{}", style(formatstr).green());
        clean().unwrap();
    } else {
        let formatstr = format!(
            "{} Testing of {} failed! Please try again.",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        clean().unwrap();
        std::process::exit(1);
    }
}

fn clean() -> Result<(), std::io::Error> {
    remove_file("temp")?;
    Ok(())
}
<commit_msg>Add color=always to rustc options<commit_after>#[macro_use]
extern crate clap;
extern crate console;
extern crate indicatif;

use clap::{App, Arg, SubCommand};
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::fs::remove_file;
use std::process::Command;

fn main() {
    let matches = App::new("r2")
        .version(crate_version!())
        .author("Olivia Hugger")
        .about("Test")
        .subcommand(SubCommand::with_name("verify").alias("v"))
        .subcommand(
            SubCommand::with_name("run")
                .alias("r")
                .arg(Arg::with_name("file").required(true).index(1)),
        ).get_matches();

    println!(r#"                 _   _ _                  "#);
    println!(r#"  _ __ _   _ ___| |_| (_)_ __   __ _ ___  "#);
    println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
    println!(r#" | |  | |_| \__ \ |_| | | | | | (_| \__ \ "#);
    println!(r#" |_|   \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
    println!(r#"                               |___/      "#);
    println!("");

    if let Some(matches) = matches.subcommand_matches("run") {
        if let Some(filename) = matches.value_of("file") {
            let bar = ProgressBar::new_spinner();
            bar.set_message(format!("Compiling {}...", filename).as_str());
            bar.enable_steady_tick(100);
            let compilecmd = Command::new("rustc")
                .args(&[filename, "-o", "temp"])
                .output()
                .expect("fail");
            bar.set_message(format!("Running {}...", filename).as_str());
            if compilecmd.status.success() {
                let runcmd = Command::new("./temp").output().expect("fail");
                bar.finish_and_clear();

                if runcmd.status.success() {
                    println!("{}", String::from_utf8_lossy(&runcmd.stdout));
                    let formatstr =
                        format!("{} Successfully ran {}", Emoji("✅", "✓"), filename);
                    println!("{}", style(formatstr).green());
                    clean().unwrap();
                } else {
                    println!("{}", String::from_utf8_lossy(&runcmd.stdout));
                    println!("{}", String::from_utf8_lossy(&runcmd.stderr));

                    let formatstr =
                        format!("{} Ran {} with errors", Emoji("⚠ ", "!"), filename);
                    println!("{}", style(formatstr).red());
                    clean().unwrap();
                }
            } else {
                bar.finish_and_clear();
                let formatstr = format!(
                    "{} Compilation of {} failed! Compiler error message:\n",
                    Emoji("⚠ ", "!"),
                    filename
                );
                println!("{}", style(formatstr).red());
                println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
                clean().unwrap();
            }
        } else {
            panic!("Please supply a filename!");
        }
    }

    if let Some(_) = matches.subcommand_matches("verify") {
        compile_only("exercises/ex1.rs");
        compile_only("exercises/ex2.rs");
        compile_only("exercises/ex3.rs");
        compile_only("exercises/ex4.rs");
        compile_only("exercises/ex5.rs");

        compile_only("exercises/variables/variables1.rs");
        compile_only("exercises/variables/variables2.rs");
        compile_only("exercises/variables/variables3.rs");
        compile_only("exercises/variables/variables4.rs");

        compile_only("exercises/functions/functions1.rs");
        compile_only("exercises/functions/functions2.rs");
        compile_only("exercises/functions/functions3.rs");
        compile_only("exercises/functions/functions4.rs");
        compile_only("exercises/functions/functions5.rs");

        compile_only("exercises/primitive_types/primitive_types1.rs");
        compile_only("exercises/primitive_types/primitive_types2.rs");
        compile_only("exercises/primitive_types/primitive_types3.rs");
        compile_only("exercises/primitive_types/primitive_types4.rs");
        compile_only("exercises/primitive_types/primitive_types5.rs");
        compile_only("exercises/primitive_types/primitive_types6.rs");

        test("exercises/tests/tests1.rs");
        test("exercises/tests/tests2.rs");
        test("exercises/tests/tests3.rs");
        test("exercises/tests/tests4.rs");

        test("exercises/if/if1.rs");

        compile_only("exercises/strings/strings1.rs");
        compile_only("exercises/strings/strings2.rs");
        compile_only("exercises/strings/strings3.rs");

        compile_only("exercises/move_semantics/move_semantics1.rs");
        compile_only("exercises/move_semantics/move_semantics2.rs");
        compile_only("exercises/move_semantics/move_semantics3.rs");
        compile_only("exercises/move_semantics/move_semantics4.rs");

        compile_only("exercises/modules/modules1.rs");
        compile_only("exercises/modules/modules2.rs");

        compile_only("exercises/macros/macros1.rs");
        compile_only("exercises/macros/macros2.rs");
        compile_only("exercises/macros/macros3.rs");
        compile_only("exercises/macros/macros4.rs");

        test("exercises/error_handling/errors1.rs");
        test("exercises/error_handling/errors2.rs");
        test("exercises/error_handling/errors3.rs");
        test("exercises/error_handling/errorsn.rs");
        compile_only("exercises/error_handling/option1.rs");
        test("exercises/error_handling/result1.rs");
    }
}

fn compile_only(filename: &str) {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Compiling {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let compilecmd = Command::new("rustc")
        .args(&[filename, "-o", "temp", "--color", "always"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if compilecmd.status.success() {
        let formatstr = format!(
            "{} Successfully compiled {}!",
            Emoji("✅", "✓"),
            filename
        );
        println!("{}", style(formatstr).green());
        clean().unwrap();
    } else {
        let formatstr = format!(
            "{} Compilation of {} failed! Compiler error message:\n",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
        clean().unwrap();
        std::process::exit(1);
    }
}

fn test(filename: &str) {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Testing {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let testcmd = Command::new("rustc")
        .args(&["--test", filename, "-o", "temp"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if testcmd.status.success() {
        let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename);
        println!("{}", style(formatstr).green());
        clean().unwrap();
    } else {
        let formatstr = format!(
            "{} Testing of {} failed! Please try again.",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        clean().unwrap();
        std::process::exit(1);
    }
}

fn clean() -> Result<(), std::io::Error> {
    remove_file("temp")?;
    Ok(())
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>playing around<commit_after>enum CellState{
    Alive,
    Dead
}

struct Cell {
    x: i8,
    y: i8,
    state: CellState
}

impl Cell {
    fn is_alive(&self) -> bool {
        match self.state {
            CellState::Alive => true,
            CellState::Dead => false
        }
    }

}

fn print_cell_data (cell: &Cell) {
    println!(
        "x: {x}, y: {y}, state: {state}",
        x=cell.x,
        y=cell.y,
        state = match cell.state {
            CellState::Dead => "dead",
            CellState::Alive => "alive"
        }
    );
}

fn main() {
    println!("Hello");
    let height = 10;
    let width = height;

    for y in 0..height {
        for x in 0..width {
            print_cell_data(&Cell{x: x, y: y, state: CellState::Alive});
        }
    }
    let cell = Cell{x: 0, y: 0, state: CellState::Alive};
    match cell {
        Cell {state: CellState::Alive, ..} => println!("cell is alive"),
        Cell {state: CellState::Dead, ..} => println!("cell is dead")
    }

    if cell.is_alive() {
        println!("We determined that cell is alive through 'is_alive' method");
    }
}

<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::os::unix::fs::MetadataExt;

#[derive(Debug)]
struct File {
    inode: u64,
    size: u64,
    path : String,
}

fn collect_files(dirs: &[String], v: &mut Vec<File>, d: &mut Vec<String>) {
    for dir in dirs {
        if let Ok(entries) = fs::read_dir(&dir) {
            for entry in entries {
                if let Ok(entry) = entry {
                    let path = entry.path();
                    if let Some(path_str) = path.to_str() {
                        if path.is_file() {
                            if let Ok(metadata) = entry.metadata() {
                                v.push(File{ inode : metadata.ino(),
                                             size  : metadata.len(),
                                             path  : String::from(path_str),
                                        })
                            }
                        } else if path.is_dir() {
                            d.push(String::from(path_str));
                        }
                    }
                }
            }
        }
        else {
            println!("{}: invalid directory", dir);
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut a = (args[1..]).to_vec();
    let mut v: Vec<File>  = Vec::new();
    loop {
        let mut d: Vec<String>   = Vec::new();
        collect_files(&a[..], &mut v, &mut d);
        if d.len() > 0 {
            a = (d[..]).to_vec();
        } else {
            break;
        }
    }
    println!("{:?}", v);
}
<commit_msg>Make the directory traversal recursive<commit_after>use std::fs;
use std::env;
use std::os::unix::fs::MetadataExt;

#[derive(Debug)]
struct File {
    inode: u64,
    size: u64,
    path : String,
}

fn collect_files(dir: &String, v: &mut Vec<File>) {
    if let Ok(entries) = fs::read_dir(dir) {
        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                if let Some(path_str) = path.to_str() {
                    if path.is_file() {
                        if let Ok(metadata) = entry.metadata() {
                            v.push(File{ inode : metadata.ino(),
                                         size  : metadata.len(),
                                         path  : String::from(path_str),
                                    })
                        }
                    } else if path.is_dir() {
                        collect_files(&(String::from(path_str)), v);
                    }
                }
            }
        }
    }
    else {
        println!("{}: invalid directory", dir);
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut v: Vec<File>  = Vec::new();

    for dir in &args[1..] {
        collect_files(dir, &mut v);
    }
    println!("{:?}", v);
}
<|endoftext|>