Refactoring
This commit is contained in:
1 parent
d5b7bc9934
commit
718afeaa40
11 files changed
+516
-454
No files matched your search
+160
-121
@@ -8,6 +8,7 @@
|
||||
// take their arguments by value, by reference, or by const reference,
|
||||
// whichever fits.
|
||||
#include <cstdio>
|
||||
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -20,10 +21,30 @@ using cliforge::Engine;
|
||||
// ---------------------------------------------------------------------
|
||||
// Custom enums -- plain C++ enum class, nothing cliforge-specific here.
|
||||
// ---------------------------------------------------------------------
|
||||
enum class ProjectTemplate { Cpp, Python, Node };
|
||||
enum class Editor { Vim, Emacs, Nano };
|
||||
enum class TwoFactorMethod { App, Sms, Email };
|
||||
enum class UpdateChannel { Stable, Beta, Nightly };
|
||||
enum class ProjectTemplate
|
||||
{
|
||||
Cpp,
|
||||
Python,
|
||||
Node
|
||||
};
|
||||
enum class Editor
|
||||
{
|
||||
Vim,
|
||||
Emacs,
|
||||
Nano
|
||||
};
|
||||
enum class TwoFactorMethod
|
||||
{
|
||||
App,
|
||||
Sms,
|
||||
Email
|
||||
};
|
||||
enum class UpdateChannel
|
||||
{
|
||||
Stable,
|
||||
Beta,
|
||||
Nightly
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Every command handler is a completely ordinary free function. There is
|
||||
@@ -33,56 +54,70 @@ enum class UpdateChannel { Stable, Beta, Nightly };
|
||||
// const reference, or reference, whichever avoids unnecessary copies.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
void projectCreate(const std::string& name, std::optional<ProjectTemplate> tmpl, bool git) {
|
||||
static const char* names[] = {"cpp", "python", "node"};
|
||||
ProjectTemplate t = tmpl.value_or(ProjectTemplate::Cpp);
|
||||
std::printf("[create] project '%s' from template '%s' (git init: %s)\n", name.c_str(),
|
||||
names[static_cast<int>(t)], git ? "yes" : "no");
|
||||
void projectCreate(const std::string& name, std::optional<ProjectTemplate> tmpl, bool git)
|
||||
{
|
||||
static const char* s_names[] = {"cpp", "python", "node"};
|
||||
ProjectTemplate t = tmpl.value_or(ProjectTemplate::Cpp);
|
||||
std::printf("[create] project '%s' from template '%s' (git init: %s)\n", name.c_str(),
|
||||
s_names[static_cast<int>(t)], git ? "yes" : "no");
|
||||
}
|
||||
|
||||
void projectDelete(const std::string& name, bool force, const std::optional<std::string>& reason) {
|
||||
std::printf("[delete] project '%s' (force=%s, reason=%s)\n", name.c_str(),
|
||||
force ? "yes" : "no", reason ? reason->c_str() : "<none given>");
|
||||
void projectDelete(const std::string& name, bool force, const std::optional<std::string>& reason)
|
||||
{
|
||||
std::printf("[delete] project '%s' (force=%s, reason=%s)\n", name.c_str(), force ? "yes" : "no",
|
||||
reason ? reason->c_str() : "<none given>");
|
||||
}
|
||||
|
||||
void projectInfo(const std::string& name, bool json) {
|
||||
if (json) {
|
||||
std::printf("{\"project\": \"%s\", \"status\": \"ok\"}\n", name.c_str());
|
||||
} else {
|
||||
std::printf("[info] project '%s': status ok\n", name.c_str());
|
||||
}
|
||||
void projectInfo(const std::string& name, bool json)
|
||||
{
|
||||
if (json)
|
||||
{
|
||||
std::printf("{\"project\": \"%s\", \"status\": \"ok\"}\n", name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf("[info] project '%s': status ok\n", name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void build(const std::vector<int32_t>& targets, bool noAssets, bool debug, bool verbose,
|
||||
const std::vector<std::string>& architectures, std::optional<Editor> editor,
|
||||
std::optional<int32_t> threshold) {
|
||||
static const char* editorNames[] = {"vim", "emacs", "nano"};
|
||||
std::printf("[build] targets=[");
|
||||
for (size_t i = 0; i < targets.size(); ++i) std::printf("%s%d", i ? "," : "", targets[i]);
|
||||
std::printf("] noAssets=%d debug=%d verbose=%d architectures=[", noAssets, debug, verbose);
|
||||
for (size_t i = 0; i < architectures.size(); ++i)
|
||||
std::printf("%s%s", i ? "," : "", architectures[i].c_str());
|
||||
std::printf("] editor=%s threshold=%s\n",
|
||||
editor ? editorNames[static_cast<int>(*editor)] : "<none>",
|
||||
threshold ? std::to_string(*threshold).c_str() : "<default>");
|
||||
const std::vector<std::string>& architectures, std::optional<Editor> editor,
|
||||
std::optional<int32_t> threshold)
|
||||
{
|
||||
static const char* s_editorNames[] = {"vim", "emacs", "nano"};
|
||||
std::printf("[build] targets=[");
|
||||
for (size_t i = 0; i < targets.size(); ++i)
|
||||
std::printf("%s%d", i ? "," : "", targets[i]);
|
||||
std::printf("] noAssets=%d debug=%d verbose=%d architectures=[", noAssets, debug, verbose);
|
||||
for (size_t i = 0; i < architectures.size(); ++i)
|
||||
std::printf("%s%s", i ? "," : "", architectures[i].c_str());
|
||||
std::printf("] editor=%s threshold=%s\n",
|
||||
editor ? s_editorNames[static_cast<int>(*editor)] : "<none>",
|
||||
threshold ? std::to_string(*threshold).c_str() : "<default>");
|
||||
}
|
||||
|
||||
void enable2fa(std::optional<TwoFactorMethod> method) {
|
||||
static const char* names[] = {"app", "sms", "email"};
|
||||
TwoFactorMethod m = method.value_or(TwoFactorMethod::App);
|
||||
std::printf("[2fa] enabled via %s\n", names[static_cast<int>(m)]);
|
||||
void enable2fa(std::optional<TwoFactorMethod> method)
|
||||
{
|
||||
static const char* s_names[] = {"app", "sms", "email"};
|
||||
TwoFactorMethod m = method.value_or(TwoFactorMethod::App);
|
||||
std::printf("[2fa] enabled via %s\n", s_names[static_cast<int>(m)]);
|
||||
}
|
||||
|
||||
void disable2fa() { std::printf("[2fa] disabled\n"); }
|
||||
|
||||
void selfUpdate(std::optional<UpdateChannel> channel, bool checkOnly) {
|
||||
static const char* names[] = {"stable", "beta", "nightly"};
|
||||
std::printf("[self-update] channel=%s checkOnly=%d\n",
|
||||
channel ? names[static_cast<int>(*channel)] : "stable(default)", checkOnly);
|
||||
void disable2fa()
|
||||
{
|
||||
std::printf("[2fa] disabled\n");
|
||||
}
|
||||
|
||||
void status(bool json) {
|
||||
std::printf(json ? "{\"status\":\"ok\"}\n" : "All systems operational.\n");
|
||||
void selfUpdate(std::optional<UpdateChannel> channel, bool checkOnly)
|
||||
{
|
||||
static const char* s_names[] = {"stable", "beta", "nightly"};
|
||||
std::printf("[self-update] channel=%s checkOnly=%d\n",
|
||||
channel ? s_names[static_cast<int>(*channel)] : "stable(default)", checkOnly);
|
||||
}
|
||||
|
||||
void status(bool json)
|
||||
{
|
||||
std::printf(json ? "{\"status\":\"ok\"}\n" : "All systems operational.\n");
|
||||
}
|
||||
|
||||
// `deploy` showcases a *loose* parameter: `hosts` is declared after
|
||||
@@ -91,10 +126,13 @@ void status(bool json) {
|
||||
// packctl deploy web1 --env prod web2 -n web3
|
||||
// still collects hosts=[web1,web2,web3], matched by position among
|
||||
// themselves, not by where they fall relative to the option/flag.
|
||||
void deploy(bool dryRun, const std::optional<std::string>& env, const std::vector<std::string>& hosts) {
|
||||
std::printf("[deploy] dryRun=%d env=%s hosts=[", dryRun, env ? env->c_str() : "<none>");
|
||||
for (size_t i = 0; i < hosts.size(); ++i) std::printf("%s%s", i ? "," : "", hosts[i].c_str());
|
||||
std::printf("]\n");
|
||||
void deploy(bool dryRun, const std::optional<std::string>& env,
|
||||
const std::vector<std::string>& hosts)
|
||||
{
|
||||
std::printf("[deploy] dryRun=%d env=%s hosts=[", dryRun, env ? env->c_str() : "<none>");
|
||||
for (size_t i = 0; i < hosts.size(); ++i)
|
||||
std::printf("%s%s", i ? "," : "", hosts[i].c_str());
|
||||
std::printf("]\n");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -103,90 +141,91 @@ void deploy(bool dryRun, const std::optional<std::string>& env, const std::vecto
|
||||
// the bound function's arguments are consistent with what was declared.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Engine cli("packctl");
|
||||
cli.describe("A demo package/project CLI built with cliforge.");
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Engine cli("packctl");
|
||||
cli.describe("A demo package/project CLI built with cliforge.");
|
||||
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.keyword("create")
|
||||
.option<ProjectTemplate>("template", 't', "Project template to scaffold (default: cpp)")
|
||||
.choices<ProjectTemplate>({{"cpp", ProjectTemplate::Cpp},
|
||||
{"python", ProjectTemplate::Python},
|
||||
{"node", ProjectTemplate::Node}})
|
||||
.flag("git", 'g', "Initialize a git repository")
|
||||
.describe("Scaffold a new project from a template")
|
||||
.action(&projectCreate);
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.keyword("create")
|
||||
.option<ProjectTemplate>("template", 't', "Project template to scaffold (default: cpp)")
|
||||
.choices<ProjectTemplate>({{"cpp", ProjectTemplate::Cpp},
|
||||
{"python", ProjectTemplate::Python},
|
||||
{"node", ProjectTemplate::Node}})
|
||||
.flag("git", 'g', "Initialize a git repository")
|
||||
.describe("Scaffold a new project from a template")
|
||||
.action(&projectCreate);
|
||||
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.keyword("delete")
|
||||
.flag("force", 'f', "Skip the confirmation prompt")
|
||||
.option<std::string>("reason", 'r', "Why the project is being deleted")
|
||||
.describe("Permanently delete a project by name")
|
||||
.action(&projectDelete);
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.keyword("delete")
|
||||
.flag("force", 'f', "Skip the confirmation prompt")
|
||||
.option<std::string>("reason", 'r', "Why the project is being deleted")
|
||||
.describe("Permanently delete a project by name")
|
||||
.action(&projectDelete);
|
||||
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.flag("json", 'j', "Print machine-readable JSON")
|
||||
.describe("Show information about a project")
|
||||
.action(&projectInfo);
|
||||
cli.command()
|
||||
.keyword("project")
|
||||
.parameter<std::string>("name", "Project name")
|
||||
.flag("json", 'j', "Print machine-readable JSON")
|
||||
.describe("Show information about a project")
|
||||
.action(&projectInfo);
|
||||
|
||||
cli.command()
|
||||
.keyword("build")
|
||||
.parameter<int32_t>("targets", "Target ids to build", /*variadic=*/true)
|
||||
.flag("no-assets", 'n', "Skip packaging assets")
|
||||
.flag("debug", 'd', "Build with debug symbols")
|
||||
.flag("verbose", 'i', "Verbose build output")
|
||||
.option<std::string>("architectures", 'a', "Target architectures", /*variadic=*/true)
|
||||
.option<Editor>("editor", 'e', "Editor to open on completion")
|
||||
.choices<Editor>({{"vim", Editor::Vim}, {"emacs", Editor::Emacs}, {"nano", Editor::Nano}})
|
||||
.option<int32_t>("threshold", 't', "Warning threshold (0 = strict)")
|
||||
.describe("Build the given target ids")
|
||||
.action(&build);
|
||||
cli.command()
|
||||
.keyword("build")
|
||||
.parameter<int32_t>("targets", "Target ids to build", /*variadic=*/true)
|
||||
.flag("no-assets", 'n', "Skip packaging assets")
|
||||
.flag("debug", 'd', "Build with debug symbols")
|
||||
.flag("verbose", 'i', "Verbose build output")
|
||||
.option<std::string>("architectures", 'a', "Target architectures", /*variadic=*/true)
|
||||
.option<Editor>("editor", 'e', "Editor to open on completion")
|
||||
.choices<Editor>({{"vim", Editor::Vim}, {"emacs", Editor::Emacs}, {"nano", Editor::Nano}})
|
||||
.option<int32_t>("threshold", 't', "Warning threshold (0 = strict)")
|
||||
.describe("Build the given target ids")
|
||||
.action(&build);
|
||||
|
||||
cli.command()
|
||||
.keyword("2fa")
|
||||
.keyword("enable")
|
||||
.option<TwoFactorMethod>("method", 'm', "Verification method (default: app)")
|
||||
.choices<TwoFactorMethod>({{"app", TwoFactorMethod::App},
|
||||
{"sms", TwoFactorMethod::Sms},
|
||||
{"email", TwoFactorMethod::Email}})
|
||||
.describe("Turn on two-factor authentication")
|
||||
.action(&enable2fa);
|
||||
cli.command()
|
||||
.keyword("2fa")
|
||||
.keyword("enable")
|
||||
.option<TwoFactorMethod>("method", 'm', "Verification method (default: app)")
|
||||
.choices<TwoFactorMethod>({{"app", TwoFactorMethod::App},
|
||||
{"sms", TwoFactorMethod::Sms},
|
||||
{"email", TwoFactorMethod::Email}})
|
||||
.describe("Turn on two-factor authentication")
|
||||
.action(&enable2fa);
|
||||
|
||||
cli.command()
|
||||
.keyword("2fa")
|
||||
.keyword("disable")
|
||||
.describe("Turn off two-factor authentication")
|
||||
.action(&disable2fa);
|
||||
cli.command()
|
||||
.keyword("2fa")
|
||||
.keyword("disable")
|
||||
.describe("Turn off two-factor authentication")
|
||||
.action(&disable2fa);
|
||||
|
||||
cli.command()
|
||||
.keyword("self-update")
|
||||
.option<UpdateChannel>("channel", 'c', "Release channel to update from")
|
||||
.choices<UpdateChannel>({{"stable", UpdateChannel::Stable},
|
||||
{"beta", UpdateChannel::Beta},
|
||||
{"nightly", UpdateChannel::Nightly}})
|
||||
.flag("check-only", 'k', "Check for updates without installing")
|
||||
.describe("Update packctl to the latest version")
|
||||
.action(&selfUpdate);
|
||||
cli.command()
|
||||
.keyword("self-update")
|
||||
.option<UpdateChannel>("channel", 'c', "Release channel to update from")
|
||||
.choices<UpdateChannel>({{"stable", UpdateChannel::Stable},
|
||||
{"beta", UpdateChannel::Beta},
|
||||
{"nightly", UpdateChannel::Nightly}})
|
||||
.flag("check-only", 'k', "Check for updates without installing")
|
||||
.describe("Update packctl to the latest version")
|
||||
.action(&selfUpdate);
|
||||
|
||||
cli.command()
|
||||
.keyword("status")
|
||||
.flag("json", 'j', "Print machine-readable JSON")
|
||||
.describe("Show whether everything is operational")
|
||||
.action(&status);
|
||||
cli.command()
|
||||
.keyword("status")
|
||||
.flag("json", 'j', "Print machine-readable JSON")
|
||||
.describe("Show whether everything is operational")
|
||||
.action(&status);
|
||||
|
||||
cli.command()
|
||||
.keyword("deploy")
|
||||
.flag("dry-run", 'n', "Show what would happen without doing it")
|
||||
.option<std::string>("env", 'e', "Environment name")
|
||||
.parameter<std::string>("hosts", "Target hostnames", /*variadic=*/true) // loose!
|
||||
.describe("Deploy to the given hosts, in any order relative to --env/-n")
|
||||
.action(&deploy);
|
||||
cli.command()
|
||||
.keyword("deploy")
|
||||
.flag("dry-run", 'n', "Show what would happen without doing it")
|
||||
.option<std::string>("env", 'e', "Environment name")
|
||||
.parameter<std::string>("hosts", "Target hostnames", /*variadic=*/true) // loose!
|
||||
.describe("Deploy to the given hosts, in any order relative to --env/-n")
|
||||
.action(&deploy);
|
||||
|
||||
return cli.run(argc, argv);
|
||||
return cli.run(argc, argv);
|
||||
}
|
||||
Reference in new issue
Block a user