Skip to content

Commit

Permalink
feature(console): add command user context support
Browse files Browse the repository at this point in the history
Current implementation implicitly forces the developer to use global variables
to enter its context during the command invocation, this change enables each
module to register a context for command to find without the need to manage
global variables.

No API breakage.

Fields added:
   esp_console_cmd_t::func_context    - (*)(int argc, char **argv, void *context)

Functions added:
   esp_err_t esp_console_cmd_set_context(const char *cmd, void *context)

Usage:

   esp_console_cmd_register(&cmd));
   esp_console_cmd_set_context(cmd.command, (void *)"context"));

Signed-off-by: Alon Bar-Lev <[email protected]>
  • Loading branch information
alonbl committed Nov 9, 2023
1 parent b90dfe0 commit 867f9d0
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 4 deletions.
32 changes: 28 additions & 4 deletions components/console/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ typedef struct cmd_item_ {
* May be NULL.
*/
char *hint;
esp_console_cmd_func_t func; //!< pointer to the command handler
void *argtable; //!< optional pointer to arg table
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
esp_console_cmd_func_t func; //!< pointer to the command handler
esp_console_cmd_func_context_t func_context; //!< pointer to the command handler
void *argtable; //!< optional pointer to arg table
void *context; //!< optional pointer to user context
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
} cmd_item_t;

/** linked list of command structures */
Expand Down Expand Up @@ -130,6 +132,7 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
}
item->argtable = cmd->argtable;
item->func = cmd->func;
item->func_context = cmd->func_context;
cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
if (last == NULL) {
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
Expand All @@ -143,6 +146,22 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
return ESP_OK;
}

esp_err_t esp_console_cmd_set_context(const char *cmd, void *context)
{
if (cmd == NULL ) {
return ESP_ERR_INVALID_ARG;
}

cmd_item_t *it;
SLIST_FOREACH(it, &s_cmd_list, next) {
if (strcmp(cmd, it->command) == 0) {
it->context = context;
return ESP_OK;
}
}
return ESP_ERR_NOT_FOUND;
}

void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
{
size_t len = strlen(buf);
Expand Down Expand Up @@ -210,7 +229,12 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
free(argv);
return ESP_ERR_NOT_FOUND;
}
*cmd_ret = (*cmd->func)(argc, argv);
if (cmd->func) {
*cmd_ret = (*cmd->func)(argc, argv);
}
if (cmd->func_context) {
*cmd_ret = (*cmd->func_context)(cmd->context, argc, argv);
}
free(argv);
return ESP_OK;
}
Expand Down
23 changes: 23 additions & 0 deletions components/console/esp_console.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ esp_err_t esp_console_deinit(void);
*/
typedef int (*esp_console_cmd_func_t)(int argc, char **argv);

/**
* @brief Console command main function
* @param context a user context given at invocation.
* @param argc number of arguments
* @param argv array with argc entries, each pointing to a zero-terminated string argument
* @return console command return code, 0 indicates "success"
*/
typedef int (*esp_console_cmd_func_context_t)(void *context, int argc, char **argv);

/**
* @brief Console command description
*/
Expand Down Expand Up @@ -188,6 +197,10 @@ typedef struct {
* Only used for the duration of esp_console_cmd_register call.
*/
void *argtable;
/**
* Pointer to a context aware function which implements the command.
*/
esp_console_cmd_func_context_t func_context;
} esp_console_cmd_t;

/**
Expand All @@ -200,6 +213,16 @@ typedef struct {
*/
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);

/**
* @brief Register context for a command
* @param cmd pointer to the command name
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_FOUND if command was not found
* - ESP_ERR_INVALID_ARG if invalid arguments
*/
esp_err_t esp_console_cmd_set_context(const char *cmd, void *context);

/**
* @brief Run command line
* @param cmdline command line (command name followed by a number of arguments)
Expand Down
65 changes: 65 additions & 0 deletions components/console/test_apps/console/main/test_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

typedef struct {
const char *in;
const char *out;
} cmd_context_t;

static int do_hello_cmd_with_context(void *context, int argc, char **argv)
{
cmd_context_t *cmd_context = (cmd_context_t *)context;
cmd_context->out = cmd_context->in;
return 0;
}

static int do_hello_cmd(int argc, char **argv)
{
printf("Hello World\n");
Expand Down Expand Up @@ -102,3 +114,56 @@ TEST_CASE("esp console init/deinit test, minimal config", "[console]")
TEST_ESP_OK(esp_console_cmd_register(&cmd));
TEST_ESP_OK(esp_console_deinit());
}

TEST_CASE("esp console test with context", "[console]")
{
/* Test with minimal init config */
esp_console_config_t console_config = {
.max_cmdline_args = 2,
.max_cmdline_length = 100,
};

TEST_ESP_OK(esp_console_init(&console_config));

const esp_console_cmd_t cmds[] = {
{
.command = "hello-c1",
.help = "Print Hello World in context c1",
.hint = NULL,
.func_context = do_hello_cmd_with_context,
},
{
.command = "hello-c2",
.help = "Print Hello World in context c2",
.hint = NULL,
.func_context = do_hello_cmd_with_context,
},
};
cmd_context_t contexts[] = {
{
.in = "c1",
.out = NULL,
},
{
.in = "c2",
.out = NULL,
},
};
TEST_ASSERT_EQUAL(sizeof(contexts) / sizeof(contexts[0]), sizeof(cmds) / sizeof(cmds[0]));

for (int i=0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
TEST_ESP_OK(esp_console_cmd_register(&cmds[i]));
TEST_ESP_OK(esp_console_cmd_set_context(cmds[i].command, &contexts[i]));
}
TEST_ASSERT_EQUAL(esp_console_cmd_set_context(NULL, NULL), ESP_ERR_INVALID_ARG);
TEST_ASSERT_EQUAL(esp_console_cmd_set_context("invalid", NULL), ESP_ERR_NOT_FOUND);

for (int i=0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
int ret;
TEST_ESP_OK(esp_console_run(cmds[i].command, &ret));
TEST_ASSERT_EQUAL(ret, 0);
TEST_ASSERT_EQUAL(contexts[i].in, contexts[i].out);
}

TEST_ESP_OK(esp_console_deinit());
}

0 comments on commit 867f9d0

Please sign in to comment.