use case_clause::case;
use clap::{Arg, ArgMatches, Command};
use std::{
collections::HashMap,
fs::{self, File},
hash::BuildHasher,
io::{ErrorKind, Write},
path::Path,
process::Command as ProcessCommand,
};
use toml_edit::{Array, DocumentMut, Formatted, Item, Value};
pub fn args_to_cargo_toml<S: BuildHasher>(
matches: &ArgMatches,
arguments: &HashMap<String, String, S>,
) -> Result<(), ErrorKind> {
let package_name = matches
.get_one::<String>("package_name")
.ok_or(ErrorKind::NotFound)?;
ProcessCommand::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(package_name)
.status()
.map_err(|err| err.kind())?;
let cargo_toml_path = Path::new(package_name).join("Cargo.toml");
let cargo_toml_content = std::fs::read_to_string(&cargo_toml_path).map_err(|err| err.kind())?;
let mut doc = cargo_toml_content
.parse::<DocumentMut>()
.map_err(|_| ErrorKind::InvalidData)?;
if let Some(package) = doc.get_mut("package") {
if let Some(maintainer) = arguments.get("maintainer") {
let mut authors_array: Array = Array::new();
authors_array.push(arguments.get("mail").map_or_else(
|| maintainer.clone(),
|mail| format!("{maintainer} <{mail}>"),
));
package["authors"] = Item::Value(Value::Array(authors_array));
}
if let Some(license) = arguments.get("license")
&& license != "TODO: License declaration"
{
package["license"] = Item::Value(Value::String(Formatted::new(license.clone())));
}
if let Some(description) = arguments.get("description")
&& description != "TODO: Package description"
{
package["description"] =
Item::Value(Value::String(Formatted::new(description.clone())));
}
}
fs::write(&cargo_toml_path, doc.to_string()).map_err(|err| err.kind())?;
Ok(())
}
pub fn args_to_package_xml<S: BuildHasher>(
arguments: &HashMap<String, String, S>,
) -> Result<(), ErrorKind> {
File::create(
Path::new(
arguments.get("package_name").ok_or(ErrorKind::NotFound)?).join("package.xml")).map_err(|err| err.kind())?.write_all(
[
r#"<?xml version="1.0"?>"#,
&format!(r#"<?xml-model href="http://download.ros.org/schema/package_format{}.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>"#,arguments.get("format").ok_or(ErrorKind::NotFound)?),
&format!(r#"<package format="{}">"#, arguments.get("format").ok_or(ErrorKind::NotFound)?),
&format!(" <name>{}</name>", Path::new(arguments.get("package_name").ok_or(ErrorKind::NotFound)?).file_name().and_then(|n| n.to_str()).ok_or(ErrorKind::NotFound)?),
" <version>0.1.0</version>",
&format!(" <description>{}</description>", arguments.get("description").ok_or(ErrorKind::NotFound)?),
&format!(r#" <maintainer email="{}">{}</maintainer>"#, arguments.get("mail").ok_or(ErrorKind::NotFound)?, arguments.get("maintainer").ok_or(ErrorKind::NotFound)?),
&format!(" <license>{}</license>", arguments.get("license").ok_or(ErrorKind::NotFound)?),
" <export>",
" <build_type>ament_cargo</build_type>",
" </export>", "</package>"
].join("\n").as_bytes()).map_err(|err| err.kind()).and_then(|()| {
println!("Created ROS Rust package `{}` with package.xml",arguments.get("package_name").ok_or(ErrorKind::NotFound)?); Ok(())
})
}
#[must_use]
pub fn env_to_matches(env_args: Vec<String>) -> ArgMatches {
Command::new("ros_new")
.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"),
).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::<Vec<String>>(),
true => env_args
)
)
}