diff --git a/src/Cmd.hpp b/src/Cmd.hpp index 6e8d6887b..63129ae29 100644 --- a/src/Cmd.hpp +++ b/src/Cmd.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Cmd/Add.hpp" #include "Cmd/Build.hpp" #include "Cmd/Clean.hpp" #include "Cmd/Fmt.hpp" diff --git a/src/Cmd/Add.cc b/src/Cmd/Add.cc new file mode 100644 index 000000000..689e71aa2 --- /dev/null +++ b/src/Cmd/Add.cc @@ -0,0 +1,200 @@ +#include "Add.hpp" + +#include "../Logger.hpp" +#include "../Manifest.hpp" +#include "../Rustify.hpp" +#include "Common.hpp" + +#include +#include +#include +#include +#include + +static int addMain(std::span args); + +const Subcmd ADD_CMD = + Subcmd{ "add" } + .setDesc("Add dependencies to poac.toml") + .setArg(Arg{ "args" } + .setDesc("Dependencies to add") + .setRequired(true) + .setVariadic(true)) + .addOpt(Opt{ "--sys" }.setDesc("Use system dependency")) + .addOpt(Opt{ "--version" }.setDesc( + "Dependency version (Only used with system-dependencies)" + )) + .addOpt( + Opt{ "--tag" }.setDesc("Specify a git tag").setPlaceholder("") + ) + .addOpt(Opt{ "--rev" } + .setDesc("Specify a git revision") + .setPlaceholder("")) + .addOpt(Opt{ "--branch" } + .setDesc("Specify a branch of the git repository") + .setPlaceholder("")) + .setMainFn(addMain); + +static Option +getNextArg( + std::span::iterator& itr, + const std::span::iterator& end +) { + if (++itr == end) { + return None; + } + return String(*itr); +} + +static inline std::optional +handleNextArg( + std::span::iterator& itr, + const std::span::iterator& end, String& arg +) { + if (const auto nextArg = getNextArg(itr, end)) { + arg = *nextArg; + return std::nullopt; + } else { + return ADD_CMD.missingArgumentForOpt(*--itr); + } +} + +static inline void +handleDependency(HashSet& newDeps, const StringRef dep) { + if (newDeps.find(dep) != newDeps.end()) { + logger::warn( + "The dependency `" + String(dep) + "` is already in the poac.toml" + ); + } else { + newDeps.insert(dep); + } +} + +static const String +getDependencyGitUrl(const StringRef dep) { + if (dep.find("://") == String::npos) { + // check if atleast in "user/repo" format + if (dep.find('/') == String::npos) { + logger::error("Invalid dependency: " + String(dep)); + return ""; + } + + return "https://github.com/" + String(dep) + ".git"; + } + return String(dep); +} + +static const String +getDependencyName(const StringRef dep) { + String name; + if (dep.find("://") == String::npos) { + name = dep.substr(dep.find_last_of('/') + 1); + } else { + name = dep.substr( + dep.find_last_of('/') + 1, dep.find(".git") - dep.find_last_of('/') - 1 + ); + } + + // Remove trailing '.git' if it exists + if (name.ends_with(".git")) { + name = name.substr(0, name.size() - 4); + } + + return name; +} + +static int +addMain(std::span args) { + if (args.empty()) { + logger::error("No dependencies to add"); + return 0; + } + + HashSet newDeps = {}; + + bool isSystemDependency = false; + String version; // Only used with system-dependencies + + String tag; + String rev; + String branch; + + for (auto itr = args.begin(); itr != args.end(); ++itr) { + if (const auto res = Cli::handleGlobalOpts(itr, args.end(), "add")) { + if (res.value() == Cli::CONTINUE) { + continue; + } else { + return res.value(); + } + } else if (*itr == "--sys") { + isSystemDependency = true; + } else if (*itr == "--version" || *itr == "-v") { + if (auto res = handleNextArg(itr, args.end(), version); res.has_value()) { + return res.value(); + } + } else if (*itr == "--tag") { + if (auto res = handleNextArg(itr, args.end(), tag); res.has_value()) { + return res.value(); + } + } else if (*itr == "--rev") { + if (auto res = handleNextArg(itr, args.end(), rev); res.has_value()) { + return res.value(); + } + } else if (*itr == "--branch") { + if (auto res = handleNextArg(itr, args.end(), branch); res.has_value()) { + return res.value(); + } + } else { + handleDependency(newDeps, *itr); + } + } + + toml::value depData = toml::table{}; + + if (isSystemDependency) { + if (version.empty()) { + logger::error("The `--version` option is required for system dependencies" + ); + return EXIT_FAILURE; + } + depData["version"] = version; + depData["system"] = true; + } else { + if (!tag.empty()) { + depData["tag"] = tag; + } + if (!rev.empty()) { + depData["rev"] = rev; + } + if (!branch.empty()) { + depData["branch"] = branch; + } + } + + auto data = toml::parse(getManifestPath()); + auto& deps = toml::find(data, "dependencies"); + + for (const auto& dep : newDeps) { + + if (!isSystemDependency) { + const String gitUrl = getDependencyGitUrl(dep); + const String depName = getDependencyName(dep); + + if (gitUrl.empty() || depName.empty()) { + return EXIT_FAILURE; + } + + deps[depName] = depData; + deps[depName]["git"] = gitUrl; + } else { + deps[String(dep)] = depData; + } + } + + std::ofstream ofs(getManifestPath()); + ofs << toml::format(data); + + logger::info("Added", "to the poac.toml"); + + return EXIT_SUCCESS; +} diff --git a/src/Cmd/Add.hpp b/src/Cmd/Add.hpp new file mode 100644 index 000000000..3525eb96c --- /dev/null +++ b/src/Cmd/Add.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "../Cli.hpp" + +extern const Subcmd ADD_CMD; diff --git a/src/main.cc b/src/main.cc index b47522d3d..aa722a559 100644 --- a/src/main.cc +++ b/src/main.cc @@ -41,6 +41,7 @@ getCli() noexcept { .setDesc("List all subcommands") .setGlobal(false) .setHidden(true)) + .addSubcmd(ADD_CMD) .addSubcmd(BUILD_CMD) .addSubcmd(CLEAN_CMD) .addSubcmd(FMT_CMD)