mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
Generate tntctl command list from exec catalog
This commit is contained in:
parent
f2be702a15
commit
8affea2508
10 changed files with 99 additions and 37 deletions
|
|
@ -37,6 +37,8 @@
|
|||
and server survival stay responsive.
|
||||
|
||||
### Changed
|
||||
- `tntctl --help` now gets its exec command list from `exec_catalog`, reducing
|
||||
duplicate command metadata between the local wrapper and SSH exec mode.
|
||||
- Updated `tnt(1)` to document the current TUI search and pager keys, and
|
||||
added script coverage to keep active help surfaces free of removed support
|
||||
commands.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ typedef enum {
|
|||
TNT_EXEC_COMMAND_TAIL,
|
||||
TNT_EXEC_COMMAND_DUMP,
|
||||
TNT_EXEC_COMMAND_POST,
|
||||
TNT_EXEC_COMMAND_EXIT
|
||||
TNT_EXEC_COMMAND_EXIT,
|
||||
TNT_EXEC_COMMAND_COUNT
|
||||
} tnt_exec_command_id_t;
|
||||
|
||||
bool exec_catalog_match(const char *line, tnt_exec_command_id_t *id,
|
||||
|
|
@ -19,6 +20,8 @@ bool exec_catalog_match(const char *line, tnt_exec_command_id_t *id,
|
|||
bool exec_catalog_args_valid(tnt_exec_command_id_t id, const char *args);
|
||||
void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||
ui_lang_t lang);
|
||||
void exec_catalog_append_command_list(char *buffer, size_t buf_size,
|
||||
size_t *pos);
|
||||
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||
tnt_exec_command_id_t id, ui_lang_t lang);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "common.h"
|
||||
|
||||
typedef enum {
|
||||
TNTCTL_TEXT_USAGE,
|
||||
TNTCTL_TEXT_INVALID_PORT,
|
||||
TNTCTL_TEXT_INVALID_LOGIN,
|
||||
TNTCTL_TEXT_INVALID_HOST_KEY_MODE,
|
||||
|
|
@ -23,6 +22,8 @@ typedef enum {
|
|||
TNTCTL_TEXT_COUNT
|
||||
} tntctl_text_id_t;
|
||||
|
||||
void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||
ui_lang_t lang);
|
||||
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id);
|
||||
|
||||
#endif /* TNTCTL_TEXT_H */
|
||||
|
|
|
|||
|
|
@ -517,6 +517,8 @@ int exec_dispatch(client_t *client) {
|
|||
return exec_command_post(client, args);
|
||||
case TNT_EXEC_COMMAND_EXIT:
|
||||
return TNT_EXIT_OK;
|
||||
case TNT_EXEC_COMMAND_COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,26 @@ void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
|||
}
|
||||
}
|
||||
|
||||
void exec_catalog_append_command_list(char *buffer, size_t buf_size,
|
||||
size_t *pos) {
|
||||
bool seen[TNT_EXEC_COMMAND_COUNT] = {0};
|
||||
size_t count = 0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); i++) {
|
||||
tnt_exec_command_id_t id = entries[i].id;
|
||||
|
||||
if (id < 0 || id >= TNT_EXEC_COMMAND_COUNT || seen[id]) {
|
||||
continue;
|
||||
}
|
||||
if (count > 0) {
|
||||
buffer_appendf(buffer, buf_size, pos, ", ");
|
||||
}
|
||||
buffer_appendf(buffer, buf_size, pos, "%s", entries[i].name);
|
||||
seen[id] = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||
tnt_exec_command_id_t id, ui_lang_t lang) {
|
||||
const exec_catalog_entry_t *entry = entry_for_id(id);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@
|
|||
#include <unistd.h>
|
||||
|
||||
static void print_usage(FILE *stream, ui_lang_t lang) {
|
||||
fputs(tntctl_text(lang, TNTCTL_TEXT_USAGE), stream);
|
||||
char output[2048];
|
||||
size_t pos = 0;
|
||||
|
||||
output[0] = '\0';
|
||||
tntctl_text_append_usage(output, sizeof(output), &pos, lang);
|
||||
fputs(output, stream);
|
||||
}
|
||||
|
||||
static void print_error(ui_lang_t lang, tntctl_text_id_t id) {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,9 @@
|
|||
#include "tntctl_text.h"
|
||||
|
||||
#include "exec_catalog.h"
|
||||
#include "i18n.h"
|
||||
|
||||
static const i18n_string_t text_catalog[TNTCTL_TEXT_COUNT] = {
|
||||
[TNTCTL_TEXT_USAGE] = I18N_STRING(
|
||||
"Usage: tntctl [options] host command [args...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -p, --port PORT SSH port (default: 2222)\n"
|
||||
" -l, --login USER SSH login name for exec identity\n"
|
||||
" --host-key-checking MODE\n"
|
||||
" OpenSSH host-key mode: yes, accept-new, no\n"
|
||||
" --known-hosts FILE OpenSSH known_hosts file\n"
|
||||
" -V, --version Print version and exit\n"
|
||||
" -h, --help Print this help and exit\n"
|
||||
"\n"
|
||||
"Commands mirror the TNT SSH exec interface: health, stats, users,\n"
|
||||
"tail, dump, post, help, and exit.\n",
|
||||
"用法: tntctl [options] host command [args...]\n"
|
||||
"\n"
|
||||
"选项:\n"
|
||||
" -p, --port PORT SSH 端口 (默认: 2222)\n"
|
||||
" -l, --login USER SSH 登录名,用作 exec 身份\n"
|
||||
" --host-key-checking MODE\n"
|
||||
" OpenSSH 主机密钥模式: yes, accept-new, no\n"
|
||||
" --known-hosts FILE OpenSSH known_hosts 文件\n"
|
||||
" -V, --version 输出版本并退出\n"
|
||||
" -h, --help 输出此帮助并退出\n"
|
||||
"\n"
|
||||
"命令对应 TNT SSH exec 接口: health, stats, users,\n"
|
||||
"tail, dump, post, help 和 exit.\n"
|
||||
),
|
||||
[TNTCTL_TEXT_INVALID_PORT] = I18N_STRING(
|
||||
"invalid port", "端口无效"
|
||||
),
|
||||
|
|
@ -79,7 +52,45 @@ static const i18n_string_t text_catalog[TNTCTL_TEXT_COUNT] = {
|
|||
)
|
||||
};
|
||||
typedef char text_catalog_must_cover_enum[
|
||||
sizeof(text_catalog) / sizeof(text_catalog[0]) == TNTCTL_TEXT_COUNT ? 1 : -1];
|
||||
sizeof(text_catalog) / sizeof(text_catalog[0]) == TNTCTL_TEXT_COUNT ? 1 : -1
|
||||
];
|
||||
|
||||
void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||
ui_lang_t lang) {
|
||||
static const i18n_string_t before_commands = I18N_STRING(
|
||||
"Usage: tntctl [options] host command [args...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -p, --port PORT SSH port (default: 2222)\n"
|
||||
" -l, --login USER SSH login name for exec identity\n"
|
||||
" --host-key-checking MODE\n"
|
||||
" OpenSSH host-key mode: yes, accept-new, no\n"
|
||||
" --known-hosts FILE OpenSSH known_hosts file\n"
|
||||
" -V, --version Print version and exit\n"
|
||||
" -h, --help Print this help and exit\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" ",
|
||||
"用法: tntctl [options] host command [args...]\n"
|
||||
"\n"
|
||||
"选项:\n"
|
||||
" -p, --port PORT SSH 端口 (默认: 2222)\n"
|
||||
" -l, --login USER SSH 登录名,用作 exec 身份\n"
|
||||
" --host-key-checking MODE\n"
|
||||
" OpenSSH 主机密钥模式: yes, accept-new, no\n"
|
||||
" --known-hosts FILE OpenSSH known_hosts 文件\n"
|
||||
" -V, --version 输出版本并退出\n"
|
||||
" -h, --help 输出此帮助并退出\n"
|
||||
"\n"
|
||||
"命令:\n"
|
||||
" "
|
||||
);
|
||||
|
||||
buffer_appendf(buffer, buf_size, pos, "%s",
|
||||
i18n_string(before_commands, lang));
|
||||
exec_catalog_append_command_list(buffer, buf_size, pos);
|
||||
buffer_appendf(buffer, buf_size, pos, "\n");
|
||||
}
|
||||
|
||||
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id) {
|
||||
if (id < 0 || id >= TNTCTL_TEXT_COUNT) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ test_manual_text: test_manual_text.c $(MANUAL_TEXT_SRC) $(COMMAND_CATALOG_SRC) $
|
|||
test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test_tntctl_text: test_tntctl_text.c $(TNTCTL_TEXT_SRC)
|
||||
test_tntctl_text: test_tntctl_text.c $(TNTCTL_TEXT_SRC) $(EXEC_CATALOG_SRC) $(COMMON_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test_ratelimit: test_ratelimit.c $(RATELIMIT_SRC) $(COMMON_SRC)
|
||||
|
|
|
|||
|
|
@ -124,6 +124,16 @@ TEST(generates_localized_usage) {
|
|||
assert(strcmp(en, "dump: usage: dump [N] | dump -n N\n") == 0);
|
||||
}
|
||||
|
||||
TEST(generates_unique_command_list) {
|
||||
char output[256] = {0};
|
||||
size_t pos = 0;
|
||||
|
||||
exec_catalog_append_command_list(output, sizeof(output), &pos);
|
||||
|
||||
assert(strcmp(output,
|
||||
"help, health, users, stats, tail, dump, post, exit") == 0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Running exec catalog unit tests...\n\n");
|
||||
|
||||
|
|
@ -131,6 +141,7 @@ int main(void) {
|
|||
RUN_TEST(matches_exec_commands_and_args);
|
||||
RUN_TEST(validates_argument_shapes);
|
||||
RUN_TEST(generates_localized_usage);
|
||||
RUN_TEST(generates_unique_command_list);
|
||||
|
||||
printf("\n✓ All %d tests passed!\n", tests_passed);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -16,15 +16,22 @@
|
|||
static int tests_passed = 0;
|
||||
|
||||
TEST(usage_matches_language) {
|
||||
const char *en = tntctl_text(UI_LANG_EN, TNTCTL_TEXT_USAGE);
|
||||
const char *zh = tntctl_text(UI_LANG_ZH, TNTCTL_TEXT_USAGE);
|
||||
char en[2048] = {0};
|
||||
char zh[2048] = {0};
|
||||
size_t en_pos = 0;
|
||||
size_t zh_pos = 0;
|
||||
|
||||
tntctl_text_append_usage(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||
tntctl_text_append_usage(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||
|
||||
assert(strstr(en, "Usage: tntctl [options] host command [args...]") != NULL);
|
||||
assert(strstr(en, "--host-key-checking MODE") != NULL);
|
||||
assert(strstr(en, "health, stats, users") != NULL);
|
||||
assert(strstr(en,
|
||||
"help, health, users, stats, tail, dump, post, exit") != NULL);
|
||||
assert(strstr(zh, "用法: tntctl [options] host command [args...]") != NULL);
|
||||
assert(strstr(zh, "OpenSSH 主机密钥模式") != NULL);
|
||||
assert(strstr(zh, "health, stats, users") != NULL);
|
||||
assert(strstr(zh,
|
||||
"help, health, users, stats, tail, dump, post, exit") != NULL);
|
||||
}
|
||||
|
||||
TEST(errors_match_language) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue