Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12,216 changes: 12,216 additions & 0 deletions 3rdparty/include/CLI/CLI11.hpp

Large diffs are not rendered by default.

17,899 changes: 17,899 additions & 0 deletions 3rdparty/include/tomlplusplus/toml.hpp

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions source/MaaPiCli/CLI/cli_args.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "cli_args.h"

#include <iostream>

// On non-Windows platforms, avoid deprecated std::wstring_convert (C++17) which
// triggers -Werror with Clang/libc++. The wcsrtombs fallback works fine on Linux.
// On Windows, keep codecvt for reliable UTF-8 <-> UTF-16 conversion.
#ifndef _WIN32
#define CLI11_HAS_CODECVT 0
#endif
#include <CLI/CLI11.hpp>

ParseResult parse_args(int argc, char** argv)
{
ParseResult result;
auto& args = result.args;

CLI::App app { "MaaPiCli - MAA Project Interface CLI" };
app.set_version_flag("-v,--version", "MaaPiCli");
app.allow_extras(false);

// ── run 子命令 ──────────────────────────────────────────────────────────
auto* sub_run = app.add_subcommand(std::string(SubcommandName::Run), "Run tasks; optionally override with a task config file");
sub_run->add_option(
"--task-config",
args.task_config_path,
"Path to task config file (TOML/JSON); if omitted, runs saved config directly");
sub_run->add_option("--progress-level", args.progress_level, "Progress output level: 0=silent, 1=task (default), 2=task+node")
->check(CLI::Range(0, 2));

// ── list 子命令 ─────────────────────────────────────────────────────────
auto* sub_list = app.add_subcommand(std::string(SubcommandName::List), "List available controllers, tasks and options");

// ── generate-config 父子命令 ────────────────────────────────────────────
auto* sub_gen = app.add_subcommand(std::string(SubcommandName::GenerateConfig), "Generate config files");
sub_gen->require_subcommand(1);

auto* sub_gen_pi = sub_gen->add_subcommand("pi", "Generate config/maa_pi_config.json");
sub_gen_pi->add_option("--controller", args.controller_name, "Controller name from interface.json (default: first, usually Android)");
sub_gen_pi->add_option(
"--adb-controller",
args.adb_controller_filter,
"Filter for ADB device auto-detection (matched against name/path/address).\n"
"Only valid when the selected controller type is Adb.");
sub_gen_pi->add_option("--resource", args.resource_name, "Resource name from interface.json (default: first)");
sub_gen_pi->add_flag("--force", args.force, "Overwrite existing file if it already exists");
sub_gen_pi->add_flag("--default-only", args.task_default_only, "Only include tasks with default_check=true");

auto* sub_gen_task = sub_gen->add_subcommand("task", "Generate a sample task config file (TOML)");
sub_gen_task->add_option("--output,-o", args.task_output_path, "Output file path (default: sample_task_config.toml)");
sub_gen_task->add_flag("--force", args.force, "Overwrite existing file if it already exists");
sub_gen_task->add_flag("--default-only", args.task_default_only, "Only include tasks with default_check=true");

// ── help 子命令 ─────────────────────────────────────────────────────────
auto* sub_help = app.add_subcommand(std::string(SubcommandName::Help), "Show help information (optionally for a subcommand)");
sub_help->add_option("command", args.help_tokens, "Subcommand to show help for")->expected(0, -1);

// 允许无子命令(交互模式)
app.require_subcommand(0, 1);

// 预生成 help 字符串供 `help <subcommand>` 子命令查表使用
result.help_map[""] = app.help();
result.help_map[std::string(SubcommandName::Run)] = sub_run->help();
result.help_map[std::string(SubcommandName::List)] = sub_list->help();
result.help_map[std::string(SubcommandName::GenerateConfig)] = sub_gen->help();
result.help_map[std::string(SubcommandName::GenerateConfigPi)] = sub_gen_pi->help();
result.help_map[std::string(SubcommandName::GenerateConfigTask)] = sub_gen_task->help();

try {
app.parse(argc, argv);
}
catch (const CLI::CallForVersion& e) {
result.args.subcommand = Subcommand::ShowVersion;
result.version_message = e.what();
return result;
}
catch (const CLI::CallForHelp& e) {
// -h/--help:让 CLI11 自动输出正确的子命令 help
app.exit(e);
result.args.subcommand = Subcommand::PrintTopHelp;
result.already_printed = true;
return result;
}
catch (const CLI::ParseError& e) {
result.success = false;
result.error_message = e.what();
return result;
}

// 确定子命令
if (sub_run->parsed()) {
args.subcommand = Subcommand::Run;
}
else if (sub_list->parsed()) {
args.subcommand = Subcommand::List;
}
else if (sub_gen_pi->parsed()) {
args.subcommand = Subcommand::GenerateConfigPi;
}
else if (sub_gen_task->parsed()) {
args.subcommand = Subcommand::GenerateConfigTask;
}
else if (sub_help->parsed()) {
args.subcommand = Subcommand::HelpCommand;
}
// else: Subcommand::None → 交互模式

return result;
}

void print_help(const ParseResult& result, const std::string& subcommand)
{
auto it = result.help_map.find(subcommand);
if (it != result.help_map.end()) {
std::cout << it->second;
return;
}

// 未知子命令名
std::cout << "Unknown subcommand: '" << subcommand << "'\n"
<< "Available subcommands: " << SubcommandName::Run << ", " << SubcommandName::List << ", " << SubcommandName::GenerateConfig
<< ", " << SubcommandName::Help << "\n";
}

67 changes: 67 additions & 0 deletions source/MaaPiCli/CLI/cli_args.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

// 子命令名集中定义,避免 cli_args.cpp / main.cpp / print_help 各处散落字面量
namespace SubcommandName
{
inline constexpr std::string_view Run = "run";
inline constexpr std::string_view List = "list";
inline constexpr std::string_view GenerateConfig = "generate-config";
inline constexpr std::string_view GenerateConfigPi = "generate-config pi";
inline constexpr std::string_view GenerateConfigTask = "generate-config task";
inline constexpr std::string_view Help = "help";
} // namespace SubcommandName

enum class Subcommand
{
None, // 无子命令 → 交互模式
Run, // run [--task-config <path>] [--progress-level <0-2>]
List, // list
GenerateConfigPi, // generate-config pi [...]
GenerateConfigTask, // generate-config task [...]
PrintTopHelp, // 由 -h/--help 触发:打印顶层 help
HelpCommand, // 由 `help [tokens...]` 子命令触发:打印指定子命令 help
ShowVersion, // -v/--version
};

struct CliArgs
{
Subcommand subcommand = Subcommand::None;

// run 子命令专有
std::string task_config_path; // --task-config <path>
int progress_level = 1; // --progress-level <0-2>,0=静默 1=Task级 2=Task+Node级

// generate-config pi 子命令专有
std::string controller_name; // --controller <name>
std::string adb_controller_filter; // --adb-controller <pattern>,过滤 ADB 设备(name/path/address 含此字符串)
std::string resource_name; // --resource <name>,按 name 指定 resource(默认取第一个)

// generate-config 共用
bool force = false; // --force:文件已存在时强制覆盖,否则退出
bool task_default_only = false; // --default-only:只处理 default_check=true 的任务(pi/task 子命令共用)

// generate-config task 专有
std::string task_output_path; // --output <path>,默认 sample_task_config.toml

// help 子命令专有
std::vector<std::string> help_tokens; // help [word ...] → 拼接为 "generate-config pi" 等
};

struct ParseResult
{
bool success = true;
bool already_printed = false; // help/version 已由 CLI11 输出,main 无需再打印
std::string error_message;
std::string version_message;
CliArgs args;
// key: "" = top-level, "run"/"list"/"generate-config" = subcommand
std::unordered_map<std::string, std::string> help_map;
};

ParseResult parse_args(int argc, char** argv);
void print_help(const ParseResult& result, const std::string& subcommand = {});
Loading
Loading