shelly 0.2.0

Generate dynamic-, scripting-language projects with dependencies for quick CLI feedback loops
Documentation
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

use tinytemplate::TinyTemplate;

use crate::config::{Context, Dependencies};

fn build_template() -> String {
    fs::read_to_string(
        PathBuf::from("src")
            .join("templates")
            .join("elixir")
            .join("mix.exs.tmpl"),
    )
    .unwrap()
}

fn source_template() -> String {
    fs::read_to_string(
        PathBuf::from("src")
            .join("templates")
            .join("elixir")
            .join("parser.ex.tmpl"),
    )
    .unwrap()
}

fn generate_templates(deps: Dependencies) -> Vec<(&'static str, String)> {
    let build = build_template();
    let source = source_template();

    let mut template = TinyTemplate::new();
    template.add_template("mix.exs", &build).unwrap();
    template.add_template("parser.ex", &source).unwrap();

    let build_template = template
        .render("mix.exs", &Context { deps: deps.clone() })
        .unwrap();

    let source_template = template
        .render("parser.ex", &Context { deps })
        .unwrap();

    let mut templates = Vec::new();

    templates.push(("mix.exs", build_template));
    templates.push(("lib/parser.ex", source_template));

    templates
}

pub fn write_project(path: String, deps: Dependencies) -> Result<(), String> {
    println!("Generating mix project...");

    let folder_path = PathBuf::from(path);

    let templates = generate_templates(deps.clone());

    let (build_path, build_template) = templates.get(0).unwrap();
    let (source_path, source_template) = templates.get(1).unwrap();

    fs::create_dir_all(folder_path.clone())
        .map_err(|err| format!("Failed to create dir: {}", err))
        .and_then(|_| {
            env::set_current_dir(folder_path.clone())
                .map_err(|err| format!("Failed to set cwd: {}", err))
        })
        .and_then(|_| {
            fs::write(build_path, build_template)
                .map_err(|err| format!("Failed to write `mix.exs`: {}", err))
        })
        .and_then(|_| {
            fs::create_dir(PathBuf::from("lib"))
                .map_err(|err| format!("Failed to create `lib` dir: {}", err))
        })
        .and_then(|_| {
            fs::write(source_path, source_template)
                .map_err(|err| format!("Failed to write `lib/parser.ex`: {}", err))
        })
}

pub fn run(shell: bool) -> Result<(), String> {
    let stdout = os_pipe::dup_stdout().unwrap();
    let mut mix = match Command::new("mix")
        .args(&["do", "deps.get,", "deps.compile"])
        .stdout(stdout)
        .spawn()
    {
        Err(error) => panic!("Failed to spawn `mix` process: {}", error),
        Ok(process) => process,
    };

    let _ = mix.wait();

    if shell {
        let stdout = os_pipe::dup_stdout().unwrap();
        let mut iex_command = match Command::new("iex")
            .args(&["-S", "mix"])
            .stdout(stdout)
            .stdin(os_pipe::dup_stdin().unwrap())
            .spawn()
        {
            Err(error) => panic!("Failed to run `iex` shell: {}", error),
            Ok(process) => process,
        };

        let _ = iex_command.wait();
    }

    Ok(())
}