use case_clause::case;
use clap::{Arg, ArgAction, ArgMatches, Command as ClapCommand};
use quick_xml::{
NsReader, Writer,
events::{BytesText, Event},
};
use std::{
fs::{File, read_to_string, write},
io::{BufReader, Cursor, ErrorKind},
};
use toml_edit::{DocumentMut, value};
pub fn env_to_matches(env_args: Vec<String>) -> ArgMatches {
ClapCommand::new("ros_add")
.version("0.1.0")
.author("GueLaKais <koroyeldiores@gmail.com>")
.about("Add dependencies to a Cargo.toml and package.xml manifest file")
.arg(
Arg::new("dependency")
.help(["Reference to a package to add as a dependency","You can reference a package by:","- `<name>`, like `cargo ros-add serde` (latest version will be used)","- `<name>@<version-req>`, like `cargo add serde@1` or `cargo add serde@=1.0.38`"].join("\n"))
.index(1)
.long("DEP_ID")
.required(true)
.value_name("DEP"),
)
.arg(Arg::new("color").help("Coloring\n \n").long("color").value_name("WHEN").value_parser(["auto", "always", "never"]))
.arg(Arg::new("no_cargo_toml").action(ArgAction::SetTrue).help("Dependency will only be added to package.xml file.").long("no-cargo-toml").required(false))
.arg(Arg::new("no_package_xml").action(ArgAction::SetTrue).help("Dependency will only be added to the Cargo.toml file.").long("no-package-xml").required(false))
.get_matches_from(
case!(
env_args.iter().any(|arg| arg == "ros-add") => env_args.iter().take(1).chain(env_args.iter().skip(2)).cloned().collect::<Vec<String>>(),
true => env_args
)
)
}
pub struct XMLHelper {
pub path: String,
pub reader: NsReader<BufReader<File>>,
pub writer: Writer<Cursor<Vec<u8>>>,
pub buf: Vec<u8>,
}
impl XMLHelper {
pub fn new(path: String) -> Result<XMLHelper, ErrorKind> {
Ok(XMLHelper {
path: path.clone(),
reader: NsReader::from_file(&path).map_err(|_| ErrorKind::NotFound)?,
writer: Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 2),
buf: Vec::new(),
})
}
}
pub fn add_dependency_to_package_xml(
path: String,
mut reader: NsReader<BufReader<File>>,
mut writer: Writer<Cursor<Vec<u8>>>,
mut buf: Vec<u8>,
dependency_name: &str,
) -> Result<(), ErrorKind> {
reader.config_mut().trim_text(true);
loop {
buf.clear();
match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) if e.name().as_ref() == b"depend" => {
writer
.write_event(Event::Start(e.clone()))
.map_err(|_| ErrorKind::WriteZero)?;
match reader.read_event_into(&mut buf) {
Ok(Event::Text(text)) => {
if reader.decoder().decode(text.as_ref()).unwrap_or_default()
== dependency_name
{
eprintln!(
"Dependency '{dependency_name}' already exists in package.xml"
);
return Err(ErrorKind::AlreadyExists);
}
writer
.write_event(Event::Text(text))
.map_err(|_| ErrorKind::WriteZero)?;
}
_ => {
println!("Everything allright")
}
}
}
Ok(Event::Start(e)) if e.name().as_ref() == b"export" => {
writer
.create_element("depend")
.write_text_content(BytesText::new(dependency_name))
.map_err(|_| ErrorKind::OutOfMemory)?;
writer
.write_event(Event::Start(e))
.map_err(|_| ErrorKind::WriteZero)?;
}
Ok(Event::Eof) => break,
Ok(e) => writer.write_event(e).map_err(|_| ErrorKind::WriteZero)?,
Err(_) => return Err(ErrorKind::InvalidData),
}
}
write(&path, writer.into_inner().into_inner()).map_err(|e| e.kind())?;
Ok(())
}
pub fn path_to_document(path: String) -> Result<(String, DocumentMut), ErrorKind> {
Ok((
path.clone(),
read_to_string(path.as_str())
.map_err(|e| e.kind())?
.parse::<DocumentMut>()
.map_err(|_| ErrorKind::InvalidData)?,
))
}
pub fn add_dependency_to_cargo_toml(
path: String,
location: String,
mut doc: DocumentMut,
dependency_name: &str,
) -> Result<(), ErrorKind> {
match doc[location.as_str()].get(dependency_name) {
Some(_) => {
eprintln!("Dependency '{dependency_name}' already exists in Cargo.toml");
Err(ErrorKind::AlreadyExists)
}
None => {
doc[location.as_str()][dependency_name] = value("*");
write(path.as_str(), doc.to_string()).map_err(|e| e.kind())?;
Ok(())
}
}
}