dockerbuild 0.1.4

Build linux binary under macOS using Docker
#[macro_use]
extern crate rust_util;

mod config;
mod build_util;
mod docker_util;

use std::fs;
use std::env;
use std::path::{Path, PathBuf};
use config::{DockerBuildConfig, DockerBuildImage};
use fs::File;
use rust_util::util_file;
use docker_util::DockerCmd;

fn main() {
    let is_display_logo = std::env::var("LOGO").ok().unwrap_or_else(|| "show".into());
    if vec!["on", "yes", "display", "show"].iter().any(|v| **v == is_display_logo) {
        println!("{}", include_str!("../dockerbuild.logo.txt")
            .replace("{color1}", "\x1B[1m\x1B[91m")
            .replace("{colore}", "\x1B[0m")
            .replace("{color2}", "\x1B[32m")
        );
    }
    information!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));

    let docker_build_config = config::load_docker_build_config_or_default();

    let mut args_iter = env::args().skip(1).peekable();

    if args_iter.peek().map(|arg| vec!["--help", "-h", "::help"].contains(&&arg.as_str())).unwrap_or(false) {
        println!("dockerbuild [::init]");
        return;
    }

    if args_iter.peek().map(|arg| arg == "::init").unwrap_or(false) {
        let docker_build_json = "dockerbuild.json";
        if let Ok(_) = File::open(docker_build_json) {
            failure!("File exists: {}", docker_build_json);
            return;
        }
        let config = DockerBuildConfig{
            image: None,
            images: Some(vec![DockerBuildImage{
                name: "linux_x64".into(),
                image: "rust".into(),
                target_dir: Some("target_linux_x64".into()),
            }, DockerBuildImage{
                name: "linux_x86".into(),
                image: "i386/rust".into(),
                target_dir: Some("target_linux_x86".into()),
            }]),
            mirror: Some("git://mirrors.ustc.edu.cn/crates.io-index".into()),
        };
        fs::write(
            docker_build_json, 
            serde_json::to_string_pretty(&config).expect("Generate file failed!")
        ).expect("Write file failed!");
        return;
    }

    let docker_build_image_opt = if let Some(first_arg) = args_iter.peek() {
        if first_arg.starts_with(":name:") {
            let name = first_arg[6..].to_string();
            args_iter.next(); // skip ':name:*'
            match &docker_build_config.images {
                None => {
                    failure!("Images is not configed: {}", &name);
                    return;
                },
                Some(images) => {
                    let docker_build_image = images.iter().find(|i| i.name == name);
                    match docker_build_image {
                        None => {
                            failure!("Image name not found: {}", &name);
                            return;
                        },
                        Some(docker_build_image) => Some(docker_build_image),
                    }
                },
            }
        } else { None }
    } else { None };

    let mut get_docker_image = || {
        if let Some(docker_build_image) = docker_build_image_opt {
            return docker_build_image.image.to_string();
        }
        if let Some(first_arg) = args_iter.peek() {
            if first_arg.starts_with(":image:") {
                let image_name = first_arg[7..].to_string();
                args_iter.next(); // skip ':image:*'
                return image_name;
            }
        }
        docker_build_config.image.clone().expect("Image is not configed in config file!")
    };
    let docker_image = get_docker_image();
    let mut get_crates_mirror = || {
        if let Some(first_arg) = args_iter.peek() {
            if first_arg.starts_with(":mirror:") {
                let mirror = first_arg[8..].to_string();
                args_iter.next(); // skip ':mirror:*'
                return Some(mirror);
            }
        }
        None
    };
    let crates_mirror = get_crates_mirror();

    let args = args_iter.map(|a| a.to_owned()).collect::<Vec<_>>();

    let mut docker_cmd = DockerCmd::new(&docker_image);
    docker_cmd.add_volumn(&get_final_docker_registry(&docker_image), "/usr/local/cargo/registry");
    if let Some(mirror) = &crates_mirror.or(docker_build_config.mirror) {
        if mirror.to_lowercase() == "none" {
            docker_cmd.mirror(&None);
        } else {
            docker_cmd.mirror(&Some(mirror));
        }
    }
    if let Some(docker_build_image) = docker_build_image_opt {
        if let Some(target_dir) = &docker_build_image.target_dir {
            docker_cmd.add_build_arg("--target-dir");
            docker_cmd.add_build_arg(target_dir);
        }
    }
    docker_cmd.exec(&args).unwrap();
}

fn get_final_docker_registry(docker_image: &str) -> String {
    let docker_registry_path_buf =get_resolved_docker_registry(docker_image).expect("Cannot find $HOME !");
    let docker_registry = docker_registry_path_buf.to_str().expect("Cannot find $HOME !!");
    check_docker_registry_exists(&docker_registry).expect("Check docker registry exists failed!");
    docker_registry.to_string()
}

fn check_docker_registry_exists(docker_registry: &str) -> Option<()> {
    let p = Path::new(docker_registry);
    if !p.exists() {
        if let Err(e) = fs::create_dir_all(&docker_registry) {
            failure!("Create dir failed: {:?}, error: {}", p, e);
            return None;
        }
    }
    Some(())
}

fn get_resolved_docker_registry(image: &str) -> Option<PathBuf> {
    util_file::get_absolute_path(&get_docker_registry(image))
}

fn get_docker_registry(image: &str) -> String {
    let mut registry = "~/.dockerbuild/register/".to_owned();
    for c in image.chars() {
        match c {
            'a'..='z' | 'A'..='Z' | '0'..='9' => registry.push(c),
            _ => registry.push('_'),
        }
    }
    registry
}

#[test]
fn test_get_docker_registry() {
    assert_eq!("~/.dockerbuild/register/rust_1_47", get_docker_registry("rust:1.47"));
}