mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 22:54:39 +08:00
exec: extract help text into catalog
This commit is contained in:
parent
5eda6ed127
commit
e911a2d469
13 changed files with 116 additions and 36 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -15,6 +15,7 @@ tests/unit/test_history_view
|
||||||
tests/unit/test_i18n
|
tests/unit/test_i18n
|
||||||
tests/unit/test_system_message
|
tests/unit/test_system_message
|
||||||
tests/unit/test_command_catalog
|
tests/unit/test_command_catalog
|
||||||
|
tests/unit/test_exec_catalog
|
||||||
tests/unit/test_help_text
|
tests/unit/test_help_text
|
||||||
tests/unit/test_manual_text
|
tests/unit/test_manual_text
|
||||||
tests/unit/test_support_text
|
tests/unit/test_support_text
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,9 @@ TNT/
|
||||||
│ ├── main.c # entry point
|
│ ├── main.c # entry point
|
||||||
│ ├── cli_text.c # startup CLI help and option text
|
│ ├── cli_text.c # startup CLI help and option text
|
||||||
│ ├── command_catalog.c # command metadata
|
│ ├── command_catalog.c # command metadata
|
||||||
|
│ ├── commands.c # COMMAND-mode command dispatch
|
||||||
|
│ ├── exec_catalog.c # SSH exec command metadata
|
||||||
|
│ ├── exec.c # SSH exec command dispatch
|
||||||
│ ├── ssh_server.c # SSH server implementation
|
│ ├── ssh_server.c # SSH server implementation
|
||||||
│ ├── bootstrap.c # SSH authentication and session bootstrap
|
│ ├── bootstrap.c # SSH authentication and session bootstrap
|
||||||
│ ├── chat_room.c # chat room logic
|
│ ├── chat_room.c # chat room logic
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
message" / "私信" instead of mixing it with "whisper" wording.
|
message" / "私信" instead of mixing it with "whisper" wording.
|
||||||
- Kept localized startup CLI syntax stable by using `用法: tnt [options]`
|
- Kept localized startup CLI syntax stable by using `用法: tnt [options]`
|
||||||
instead of localizing the `[options]` metavariable.
|
instead of localizing the `[options]` metavariable.
|
||||||
|
- Moved SSH exec help rows into an `exec_catalog` module so command metadata
|
||||||
|
no longer lives as one large translated blob inside the shared i18n table.
|
||||||
- Renamed the internal language state from help-oriented names to
|
- Renamed the internal language state from help-oriented names to
|
||||||
UI-language names (`ui_lang_t`, `client->ui_lang`, and
|
UI-language names (`ui_lang_t`, `client->ui_lang`, and
|
||||||
`i18n_*_ui_lang`) so future i18n work has a correctly named seam.
|
`i18n_*_ui_lang`) so future i18n work has a correctly named seam.
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ src/
|
||||||
├── input.c - Interactive session loop and key handling
|
├── input.c - Interactive session loop and key handling
|
||||||
├── commands.c - COMMAND-mode command dispatch
|
├── commands.c - COMMAND-mode command dispatch
|
||||||
├── command_catalog.c - COMMAND-mode names, aliases, and help summaries
|
├── command_catalog.c - COMMAND-mode names, aliases, and help summaries
|
||||||
|
├── exec_catalog.c - SSH exec command help metadata
|
||||||
├── exec.c - SSH exec command dispatch
|
├── exec.c - SSH exec command dispatch
|
||||||
├── chat_room.c - Chat room logic and message broadcasting
|
├── chat_room.c - Chat room logic and message broadcasting
|
||||||
├── message.c - Message persistence (RFC3339 format)
|
├── message.c - Message persistence (RFC3339 format)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ STRUCTURE
|
||||||
src/bootstrap.c SSH auth/session bootstrap
|
src/bootstrap.c SSH auth/session bootstrap
|
||||||
src/chat_room.c broadcast and room state
|
src/chat_room.c broadcast and room state
|
||||||
src/commands.c COMMAND-mode command dispatch
|
src/commands.c COMMAND-mode command dispatch
|
||||||
|
src/exec_catalog.c SSH exec command metadata
|
||||||
src/exec.c SSH exec command dispatch
|
src/exec.c SSH exec command dispatch
|
||||||
src/message.c persistence, search
|
src/message.c persistence, search
|
||||||
src/history_view.c message viewport / scroll state
|
src/history_view.c message viewport / scroll state
|
||||||
|
|
|
||||||
9
include/exec_catalog.h
Normal file
9
include/exec_catalog.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef EXEC_CATALOG_H
|
||||||
|
#define EXEC_CATALOG_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||||
|
ui_lang_t lang);
|
||||||
|
|
||||||
|
#endif /* EXEC_CATALOG_H */
|
||||||
|
|
@ -53,7 +53,6 @@ typedef enum {
|
||||||
I18N_UNKNOWN_COMMAND_FORMAT,
|
I18N_UNKNOWN_COMMAND_FORMAT,
|
||||||
I18N_DID_YOU_MEAN_FORMAT,
|
I18N_DID_YOU_MEAN_FORMAT,
|
||||||
I18N_UNKNOWN_GUIDANCE,
|
I18N_UNKNOWN_GUIDANCE,
|
||||||
I18N_EXEC_HELP,
|
|
||||||
I18N_EXEC_USERS_USAGE,
|
I18N_EXEC_USERS_USAGE,
|
||||||
I18N_EXEC_STATS_USAGE,
|
I18N_EXEC_STATS_USAGE,
|
||||||
I18N_EXEC_TAIL_USAGE,
|
I18N_EXEC_TAIL_USAGE,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "chat_room.h"
|
#include "chat_room.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "exec_catalog.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
@ -116,9 +117,13 @@ static void resolve_exec_username(const client_t *client, char *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exec_command_help(client_t *client) {
|
static int exec_command_help(client_t *client) {
|
||||||
const char *help_text = i18n_text(client->ui_lang, I18N_EXEC_HELP);
|
char help_text[1024];
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
return client_send(client, help_text, strlen(help_text)) == 0 ? 0 : 1;
|
help_text[0] = '\0';
|
||||||
|
exec_catalog_append_help(help_text, sizeof(help_text), &pos,
|
||||||
|
client->ui_lang);
|
||||||
|
return client_send(client, help_text, pos) == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exec_command_health(client_t *client) {
|
static int exec_command_health(client_t *client) {
|
||||||
|
|
|
||||||
35
src/exec_catalog.c
Normal file
35
src/exec_catalog.c
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "exec_catalog.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *usage;
|
||||||
|
const char *summary_en;
|
||||||
|
const char *summary_zh;
|
||||||
|
} exec_catalog_entry_t;
|
||||||
|
|
||||||
|
static const exec_catalog_entry_t entries[] = {
|
||||||
|
{"help", "Show this help", "显示此帮助"},
|
||||||
|
{"health", "Print service health", "输出服务健康状态"},
|
||||||
|
{"users [--json]", "List online users", "列出在线用户"},
|
||||||
|
{"stats [--json]", "Print room statistics", "输出房间统计"},
|
||||||
|
{"tail [N]", "Print recent messages", "输出最近消息"},
|
||||||
|
{"tail -n N", "Print recent messages", "输出最近消息"},
|
||||||
|
{"post MESSAGE", "Post a message non-interactively", "非交互发送消息"},
|
||||||
|
{"post \"/me act\"", "Post an action message", "发送动作消息"},
|
||||||
|
{"exit", "Exit successfully", "成功退出"}
|
||||||
|
};
|
||||||
|
|
||||||
|
void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||||
|
ui_lang_t lang) {
|
||||||
|
if (lang == UI_LANG_ZH) {
|
||||||
|
buffer_appendf(buffer, buf_size, pos, "TNT exec 接口\n命令:\n");
|
||||||
|
} else {
|
||||||
|
buffer_appendf(buffer, buf_size, pos, "TNT exec interface\nCommands:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); i++) {
|
||||||
|
const char *summary = lang == UI_LANG_ZH ? entries[i].summary_zh
|
||||||
|
: entries[i].summary_en;
|
||||||
|
buffer_appendf(buffer, buf_size, pos, " %-15s %s\n",
|
||||||
|
entries[i].usage, summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -208,30 +208,6 @@ static const i18n_text_entry_t text_catalog[I18N_TEXT_COUNT] = {
|
||||||
"Type :help for help\n",
|
"Type :help for help\n",
|
||||||
"输入 :help 查看帮助\n"
|
"输入 :help 查看帮助\n"
|
||||||
},
|
},
|
||||||
[I18N_EXEC_HELP] = {
|
|
||||||
"TNT exec interface\n"
|
|
||||||
"Commands:\n"
|
|
||||||
" help Show this help\n"
|
|
||||||
" health Print service health\n"
|
|
||||||
" users [--json] List online users\n"
|
|
||||||
" stats [--json] Print room statistics\n"
|
|
||||||
" tail [N] Print recent messages\n"
|
|
||||||
" tail -n N Print recent messages\n"
|
|
||||||
" post MESSAGE Post a message non-interactively\n"
|
|
||||||
" post \"/me act\" Post an action message\n"
|
|
||||||
" exit Exit successfully\n",
|
|
||||||
"TNT exec 接口\n"
|
|
||||||
"命令:\n"
|
|
||||||
" help 显示此帮助\n"
|
|
||||||
" health 输出服务健康状态\n"
|
|
||||||
" users [--json] 列出在线用户\n"
|
|
||||||
" stats [--json] 输出房间统计\n"
|
|
||||||
" tail [N] 输出最近消息\n"
|
|
||||||
" tail -n N 输出最近消息\n"
|
|
||||||
" post MESSAGE 非交互发送消息\n"
|
|
||||||
" post \"/me act\" 发送动作消息\n"
|
|
||||||
" exit 成功退出\n"
|
|
||||||
},
|
|
||||||
[I18N_EXEC_USERS_USAGE] = {
|
[I18N_EXEC_USERS_USAGE] = {
|
||||||
"users: usage: users [--json]\n",
|
"users: usage: users [--json]\n",
|
||||||
"users: 用法: users [--json]\n"
|
"users: 用法: users [--json]\n"
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ CHAT_ROOM_SRC = ../../src/chat_room.c
|
||||||
HISTORY_VIEW_SRC = ../../src/history_view.c
|
HISTORY_VIEW_SRC = ../../src/history_view.c
|
||||||
I18N_SRC = ../../src/i18n.c
|
I18N_SRC = ../../src/i18n.c
|
||||||
I18N_TEXT_SRC = ../../src/i18n_text.c
|
I18N_TEXT_SRC = ../../src/i18n_text.c
|
||||||
|
EXEC_CATALOG_SRC = ../../src/exec_catalog.c
|
||||||
SYSTEM_MESSAGE_SRC = ../../src/system_message.c
|
SYSTEM_MESSAGE_SRC = ../../src/system_message.c
|
||||||
HELP_TEXT_SRC = ../../src/help_text.c
|
HELP_TEXT_SRC = ../../src/help_text.c
|
||||||
MANUAL_TEXT_SRC = ../../src/manual_text.c
|
MANUAL_TEXT_SRC = ../../src/manual_text.c
|
||||||
RATELIMIT_SRC = ../../src/ratelimit.c
|
RATELIMIT_SRC = ../../src/ratelimit.c
|
||||||
|
|
||||||
TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_command_catalog test_help_text test_manual_text test_cli_text test_ratelimit
|
TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_command_catalog test_exec_catalog test_help_text test_manual_text test_cli_text test_ratelimit
|
||||||
|
|
||||||
.PHONY: all clean run
|
.PHONY: all clean run
|
||||||
|
|
||||||
|
|
@ -51,6 +52,9 @@ test_system_message: test_system_message.c $(SYSTEM_MESSAGE_SRC) $(I18N_SRC) $(I
|
||||||
test_command_catalog: test_command_catalog.c $(COMMAND_CATALOG_SRC) $(COMMON_SRC)
|
test_command_catalog: test_command_catalog.c $(COMMAND_CATALOG_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
test_exec_catalog: test_exec_catalog.c $(EXEC_CATALOG_SRC) $(COMMON_SRC)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
test_help_text: test_help_text.c $(HELP_TEXT_SRC) $(COMMAND_CATALOG_SRC) $(COMMON_SRC)
|
test_help_text: test_help_text.c $(HELP_TEXT_SRC) $(COMMAND_CATALOG_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
|
|
@ -85,6 +89,9 @@ run: all
|
||||||
@echo "=== Running Command Catalog Tests ==="
|
@echo "=== Running Command Catalog Tests ==="
|
||||||
./test_command_catalog
|
./test_command_catalog
|
||||||
@echo ""
|
@echo ""
|
||||||
|
@echo "=== Running Exec Catalog Tests ==="
|
||||||
|
./test_exec_catalog
|
||||||
|
@echo ""
|
||||||
@echo "=== Running Help Text Tests ==="
|
@echo "=== Running Help Text Tests ==="
|
||||||
./test_help_text
|
./test_help_text
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
|
||||||
49
tests/unit/test_exec_catalog.c
Normal file
49
tests/unit/test_exec_catalog.c
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* Unit tests for SSH exec command help catalog */
|
||||||
|
|
||||||
|
#include "../../include/exec_catalog.h"
|
||||||
|
#include "text_assert.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TEST(name) static void test_##name()
|
||||||
|
#define RUN_TEST(name) do { \
|
||||||
|
printf("Running %s... ", #name); \
|
||||||
|
test_##name(); \
|
||||||
|
printf("✓\n"); \
|
||||||
|
tests_passed++; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static int tests_passed = 0;
|
||||||
|
|
||||||
|
TEST(generates_localized_exec_help) {
|
||||||
|
char en[2048] = {0};
|
||||||
|
char zh[2048] = {0};
|
||||||
|
size_t en_pos = 0;
|
||||||
|
size_t zh_pos = 0;
|
||||||
|
|
||||||
|
exec_catalog_append_help(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||||
|
exec_catalog_append_help(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||||
|
|
||||||
|
assert(strstr(en, "TNT exec interface") != NULL);
|
||||||
|
assert(strstr(en, "Commands:") != NULL);
|
||||||
|
assert(strstr(en, "users [--json]") != NULL);
|
||||||
|
assert(strstr(en, "post MESSAGE") != NULL);
|
||||||
|
assert(strstr(en, "support") == NULL);
|
||||||
|
|
||||||
|
assert(strstr(zh, "TNT exec 接口") != NULL);
|
||||||
|
assert(strstr(zh, "命令:") != NULL);
|
||||||
|
assert(strstr(zh, "users [--json]") != NULL);
|
||||||
|
assert(strstr(zh, "post MESSAGE") != NULL);
|
||||||
|
assert(strstr(zh, "support") == NULL);
|
||||||
|
assert_ascii_angle_placeholders(zh);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("Running exec catalog unit tests...\n\n");
|
||||||
|
|
||||||
|
RUN_TEST(generates_localized_exec_help);
|
||||||
|
|
||||||
|
printf("\n✓ All %d tests passed!\n", tests_passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -141,14 +141,6 @@ TEST(text_lookup_matches_language) {
|
||||||
"Unknown command") != NULL);
|
"Unknown command") != NULL);
|
||||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_UNKNOWN_COMMAND_FORMAT),
|
assert(strstr(i18n_text(UI_LANG_ZH, I18N_UNKNOWN_COMMAND_FORMAT),
|
||||||
"未知命令") != NULL);
|
"未知命令") != NULL);
|
||||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_HELP),
|
|
||||||
"TNT exec interface") != NULL);
|
|
||||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_HELP),
|
|
||||||
"support") == NULL);
|
|
||||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_HELP),
|
|
||||||
"TNT exec 接口") != NULL);
|
|
||||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_HELP),
|
|
||||||
"support") == NULL);
|
|
||||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_POST_EMPTY),
|
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_POST_EMPTY),
|
||||||
"message cannot be empty") != NULL);
|
"message cannot be empty") != NULL);
|
||||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_POST_EMPTY),
|
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_POST_EMPTY),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue