ros_new 0.1.0

little cargo plugin to produce cargo projects with `package.xml`
use case_clause::case;
use std::{
    env,
    fs::File,
    io::{ErrorKind, Write},
    path::Path,
    process::Command,
};

use clap::{Arg, ArgMatches, Command as ClapCommand};

fn env_to_matches(env_args: Vec<String>) -> ArgMatches {
    ClapCommand::new("cargo")
        .version("0.1.0")
        .author("GueLaKais <koroyeldiores@gmail.com>")
        .about("Creates a new Rust package with ROS package.xml")
        .arg(
            Arg::new("package_name")
                .required(true)
                .help("Name of the package to create")
                .index(1),
        ).arg(
            Arg::new("vcs")
                .long("vcs")
                .help("Initialize a new repository for the given version control system, overriding a global configuration. [possible values: git, hg, pijul, fossil, none]")
                .value_name("VCS").value_parser(["git", "hg", "pijul", "fossil", "none"])
        ).arg(
            Arg::new("edition")
                .long("edition")
                .help(
                    "Edition to set for the crate generated [possible values: 2015, 2018, 2021, 2024]"
                )
                .value_name("YEAR")
                .value_parser(["2015", "2018", "2021", "2024"])
        )
        .arg(
            Arg::new("bin")
                .long("bin")
                .help("Use a binary (application) template [default]")
                .action(clap::ArgAction::SetTrue),
        )
        .arg(
            Arg::new("lib")
                .long("lib")
                .help("Use a library template")
                .action(clap::ArgAction::SetTrue),
        )
        .arg(
            Arg::new("mail")
                .long("mail")
                .default_value("you@email.com")
                .help("Adds the E-mail Address of the maintainer"),
        )
        .arg(
            Arg::new("maintainer")
                .long("maintainer")
                .default_value("user")
                .help("The name of the maintainer"),
        )
        .arg(
            Arg::new("license")
                .long("license")
                .default_value("TODO: License declaration")
                .help("Declares the used License of this Package"),
        )
        .arg(
            Arg::new("format")
                .long("format")
                .default_value("3")
                .help("Declares the ROS package format"),
        )
        .arg(
            Arg::new("description")
                .long("description")
                .default_value("TODO: Package description")
                .help("Package Description"),
        )
        .get_matches_from(
                case!(
                    env_args.iter().any(|arg| arg == "ros-new")=> env_args.iter().take(1).chain(env_args.iter().skip(2)).cloned().collect(), 
                    true => env_args
                )
            )
}

fn main() -> Result<(), Box<ErrorKind>> {
    // When invoked as `cargo ros-new`, the first argument is "ros-new"
    // So we need to skip it if present
    let matches: ArgMatches = env_to_matches(env::args().collect());

    let arguments = [matches
        .get_one::<String>("package_name")
        .ok_or(ErrorKind::NotFound)?
        .clone()]
    .to_vec()
    .into_iter()
    .chain(
        ["format", "description", "mail", "maintainer", "license"]
            .iter()
            .map(|argument| matches.get_one::<String>(argument).unwrap().clone()),
    )
    .collect::<Vec<String>>();

    Command::new("cargo")
        .arg("new")
        .args(
            matches
                .get_one::<String>("edition")
                .into_iter()
                .flat_map(|e| ["--edition", e]),
        )
        .args(
            matches
                .get_one::<String>("vcs")
                .into_iter()
                .flat_map(|v| ["--vcs", v]),
        )
        .arg(if matches.get_flag("lib") {
            "--lib"
        } else {
            "--bin"
        })
        .arg(&arguments[0])
        .status()
        .map_err(|err| err.kind())?;

    File::create(Path::new(&arguments[0]).join("package.xml"))
        .map_err(|err| err.kind())?
        .write_all(
            [
                r#"<?xml version="1.0"?>"#,
                r#"<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>"#,
                &format!(r#"<package format="{}">"#,arguments[1]),
                &format!("  <name>{}</name>",arguments[0]),
                "  <version>0.1.0</version>",
                &format!("  <description>{}</description>",arguments[2]),
                &format!(r#"  <maintainer email="{}">{}</maintainer>"#,arguments[3],arguments[4]),
                &format!("  <license>{}</license>",arguments[5]),
                "  <export>",
                "    <build_type>ament_cargo</build_type>",
                "  </export>",
                "</package>",
            ]
            .join("\n")
            .as_bytes(),
        )
        .map_err(|err| err.kind())?;

    println!(
        "Created ROS Rust package `{}` with package.xml",
        arguments[0]
    );
    Ok(())
}