From d8c694a43efbd237a05dfa8fb94605f3334a2f46 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 24 May 2026 20:20:06 +0100 Subject: [PATCH] command: add a hierarchy of classes to represent different types of commands --- bshell/command/alias.c | 133 +++++++++++++++++++ bshell/command/alias.h | 35 +++++ bshell/command/cmdlet.c | 60 +++++++++ bshell/command/cmdlet.h | 25 ++++ bshell/command/command.c | 272 ++++++++++++++++++++++++++++++++++++++ bshell/command/command.h | 62 +++++++++ bshell/command/function.c | 0 bshell/command/function.h | 23 ++++ 8 files changed, 610 insertions(+) create mode 100644 bshell/command/alias.c create mode 100644 bshell/command/alias.h create mode 100644 bshell/command/cmdlet.c create mode 100644 bshell/command/cmdlet.h create mode 100644 bshell/command/command.c create mode 100644 bshell/command/command.h create mode 100644 bshell/command/function.c create mode 100644 bshell/command/function.h diff --git a/bshell/command/alias.c b/bshell/command/alias.c new file mode 100644 index 0000000..dacae26 --- /dev/null +++ b/bshell/command/alias.c @@ -0,0 +1,133 @@ +#include "alias.h" + +#include "../status.h" +#include "command.h" + +#include +#include +#include + +struct bshell_alias_p { +}; + +static void init(fx_object *obj, void *priv) +{ +} + +enum bshell_status bshell_alias_get_callable_name( + const bshell_alias *alias, + fx_string *out) +{ + bshell_alias_class *c = fx_object_get_interface( + alias, + BSHELL_TYPE_ALIAS); + if (!c) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + const char *callable_name = c->a_callable_name; + if (c->a_get_callable_name) { + callable_name = c->a_get_callable_name(alias); + } + + if (callable_name) { + fx_string_append_cstr(out, callable_name); + } + + return BSHELL_SUCCESS; +} + +enum bshell_status bshell_alias_get_target_name( + const bshell_alias *alias, + fx_string *out) +{ + bshell_alias_class *c = fx_object_get_interface( + alias, + BSHELL_TYPE_ALIAS); + if (!c) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + const char *target_name = c->a_target_name; + if (c->a_get_target_name) { + target_name = c->a_get_target_name(alias); + } + + if (target_name) { + fx_string_append_cstr(out, target_name); + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status get_callable_name_static( + const fx_type *ty, + fx_string *out) +{ + bshell_alias_class *c = fx_type_get_interface(ty, BSHELL_TYPE_ALIAS); + if (!c) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + if (c->a_callable_name) { + fx_string_append_cstr(out, c->a_callable_name); + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status get_description( + const bshell_command *cmd, + fx_string *out) +{ + bshell_alias_class *c = fx_object_get_interface(cmd, BSHELL_TYPE_ALIAS); + if (!c) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + const char *callable_name = c->a_callable_name; + if (c->a_get_callable_name) { + callable_name = c->a_get_callable_name(cmd); + } + + const char *target_name = c->a_target_name; + if (c->a_get_target_name) { + target_name = c->a_get_target_name(cmd); + } + + fx_string_append_cstr(out, callable_name); + fx_string_append_cstr(out, " -> "); + fx_string_append_cstr(out, target_name); + return BSHELL_SUCCESS; +} + +enum bshell_status get_description_static(const fx_type *ty, fx_string *out) +{ + return BSHELL_SUCCESS; +} + +FX_TYPE_CLASS_BEGIN(bshell_alias) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + FX_INTERFACE_ENTRY(c_command_type) = "Alias"; + FX_INTERFACE_ENTRY(c_get_callable_name) + = bshell_alias_get_callable_name; + FX_INTERFACE_ENTRY(c_get_callable_name_static) + = get_callable_name_static; + FX_INTERFACE_ENTRY(c_get_description) = get_description; + FX_INTERFACE_ENTRY(c_get_description_static) + = get_description_static; + FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND) +FX_TYPE_CLASS_END(bshell_alias) + +FX_TYPE_DEFINITION_BEGIN(bshell_alias) + FX_TYPE_ID(0x1736c10e, 0xebe6, 0x4ba5, 0x83d9, 0x5da3b7dc706c); + FX_TYPE_NAME("bshell.alias"); + FX_TYPE_EXTENDS(BSHELL_TYPE_COMMAND); + FX_TYPE_CLASS(bshell_alias_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_alias_p); +FX_TYPE_DEFINITION_END(bshell_alias) diff --git a/bshell/command/alias.h b/bshell/command/alias.h new file mode 100644 index 0000000..11ef3ac --- /dev/null +++ b/bshell/command/alias.h @@ -0,0 +1,35 @@ +#ifndef COMMAND_ALIAS_H_ +#define COMMAND_ALIAS_H_ + +#include "../verb.h" + +#include +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_ALIAS (bshell_alias_get_type()) + +FX_DECLARE_TYPE(bshell_alias); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_alias) + const char *a_callable_name; + const char *a_target_name; + const char *(*a_get_callable_name)(const bshell_alias *); + const char *(*a_get_target_name)(const bshell_alias *); +FX_TYPE_CLASS_DECLARATION_END(bshell_alias) + +extern fx_type_id bshell_alias_get_type(void); + +extern enum bshell_status bshell_alias_get_callable_name( + const bshell_alias *alias, + fx_string *out); +extern enum bshell_status bshell_alias_get_target_name( + const bshell_alias *alias, + fx_string *out); + +FX_DECLS_END; + +#endif diff --git a/bshell/command/cmdlet.c b/bshell/command/cmdlet.c new file mode 100644 index 0000000..5dd16b8 --- /dev/null +++ b/bshell/command/cmdlet.c @@ -0,0 +1,60 @@ +#include "cmdlet.h" + +#include "../status.h" +#include "command.h" + +#include +#include +#include + +struct bshell_cmdlet_p { +}; + +static void init(fx_object *obj, void *priv) +{ +} + +static enum bshell_status get_callable_name_static( + const fx_type *ty, + fx_string *out) +{ + bshell_cmdlet_class *class = fx_type_get_interface( + ty, + BSHELL_TYPE_CMDLET); + if (!class) { + return BSHELL_ERR_NOT_SUPPORTED; + } + + const bshell_verb *verb_info = bshell_verb_get_by_id(class->c_verb); + if (!verb_info) { + return BSHELL_ERR_BAD_STATE; + } + + fx_string_append_cstrf( + out, + "%s-%s", + bshell_verb_get_name(verb_info), + class->c_noun); + return BSHELL_SUCCESS; +} + +FX_TYPE_CLASS_BEGIN(bshell_cmdlet) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + FX_INTERFACE_ENTRY(c_command_type) = "Cmdlet"; + FX_INTERFACE_ENTRY(c_get_callable_name_static) + = get_callable_name_static; + FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND) +FX_TYPE_CLASS_END(bshell_cmdlet) + +FX_TYPE_DEFINITION_BEGIN(bshell_cmdlet) + FX_TYPE_ID(0xc71f4d59, 0x8066, 0x4294, 0xa6b0, 0xe1f3eb04f454); + FX_TYPE_NAME("bshell.cmdlet"); + FX_TYPE_EXTENDS(BSHELL_TYPE_COMMAND); + FX_TYPE_CLASS(bshell_cmdlet_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_cmdlet_p); +FX_TYPE_DEFINITION_END(bshell_cmdlet) diff --git a/bshell/command/cmdlet.h b/bshell/command/cmdlet.h new file mode 100644 index 0000000..be56719 --- /dev/null +++ b/bshell/command/cmdlet.h @@ -0,0 +1,25 @@ +#ifndef COMMAND_CMDLET_H_ +#define COMMAND_CMDLET_H_ + +#include "../verb.h" + +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_CMDLET (bshell_cmdlet_get_type()) + +FX_DECLARE_TYPE(bshell_cmdlet); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_cmdlet) + enum bshell_verb_id c_verb; + const char *c_noun; +FX_TYPE_CLASS_DECLARATION_END(bshell_cmdlet) + +extern fx_type_id bshell_cmdlet_get_type(void); + +FX_DECLS_END; + +#endif diff --git a/bshell/command/command.c b/bshell/command/command.c new file mode 100644 index 0000000..79bbc51 --- /dev/null +++ b/bshell/command/command.c @@ -0,0 +1,272 @@ +#include "command.h" + +#include "../runtime/pipeline.h" +#include "../status.h" + +#include +#include +#include +#include +#include + +struct bshell_command_p { + fx_value *cmd_args; + size_t cmd_nr_args; + bool cmd_args_reversed; +}; + +static void init(fx_object *obj, void *priv) +{ +} + +static void command_set_args( + struct bshell_command_p *cmd, + fx_value *args, + size_t nr_args) +{ + cmd->cmd_args = args; + cmd->cmd_nr_args = nr_args; + cmd->cmd_args_reversed = false; +} + +static void command_set_args_reverse( + struct bshell_command_p *cmd, + fx_value *args, + size_t nr_args) +{ + cmd->cmd_args = args; + cmd->cmd_nr_args = nr_args; + cmd->cmd_args_reversed = true; +} + +static const fx_value *command_get_arg( + struct bshell_command_p *cmd, + size_t index) +{ + if (index >= cmd->cmd_nr_args) { + return NULL; + } + + if (cmd->cmd_args_reversed) { + return &cmd->cmd_args[cmd->cmd_nr_args - index - 1]; + } + + return &cmd->cmd_args[index]; +} + +static fx_status get_command_type( + const fx_value *cmd_v, + const fx_property *prop, + fx_value *out) +{ + bshell_command *cmd = NULL; + fx_value_get_object(cmd_v, &cmd); + bshell_command_class *cmd_class = fx_object_get_interface( + cmd, + BSHELL_TYPE_COMMAND); + + *out = FX_CSTR(cmd_class->c_command_type); + return FX_SUCCESS; +} + +static fx_status get_name( + const fx_value *cmd_v, + const fx_property *prop, + fx_value *out) +{ + bshell_command *cmd = NULL; + fx_value_get_object(cmd_v, &cmd); + bshell_command_class *cmd_class = fx_object_get_interface( + cmd, + BSHELL_TYPE_COMMAND); + + fx_string *result = fx_string_create(); + const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd)); + + if (cmd_class->c_get_description) { + cmd_class->c_get_description(cmd, result); + } else if (cmd_class->c_get_description_static) { + cmd_class->c_get_description_static(ty, result); + } else if (cmd_class->c_get_callable_name) { + cmd_class->c_get_callable_name(cmd, result); + } else if (cmd_class->c_get_callable_name_static) { + cmd_class->c_get_callable_name_static(ty, result); + } + + *out = FX_VALUE_OBJECT(result); + return FX_SUCCESS; +} + +static fx_status get_version( + const fx_value *cmd_v, + const fx_property *prop, + fx_value *out) +{ + bshell_command *cmd = NULL; + fx_value_get_object(cmd_v, &cmd); + const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd)); + const fx_assembly *assembly = fx_type_get_assembly(ty); + + long major = 0, minor = 0, build = 0, revision = 0; + fx_assembly_get_version(assembly, &major, &minor, &build, &revision); + + fx_string *result = fx_string_create(); + if (revision != 0) { + fx_string_append_cstrf( + result, + "%ld.%ld.%ld.%ld", + major, + minor, + build, + revision); + } else { + fx_string_append_cstrf( + result, + "%ld.%ld.%ld", + major, + minor, + build); + } + + *out = FX_VALUE_OBJECT(result); + return FX_SUCCESS; +} + +static fx_status get_source( + const fx_value *cmd_v, + const fx_property *prop, + fx_value *out) +{ + bshell_command *cmd = NULL; + fx_value_get_object(cmd_v, &cmd); + const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd)); + const fx_assembly *assembly = fx_type_get_assembly(ty); + + *out = FX_CSTR(fx_assembly_get_name(assembly)); + return FX_SUCCESS; +} + +extern const fx_assembly *bshell_assembly_get(void); + +bshell_command *bshell_command_find_static(const char *name) +{ + fx_string *target_name = fx_string_create(); + fx_string *call_name = fx_string_create_from_cstr(name); + fx_string_transform_lowercase(call_name); + + const fx_assembly *self = bshell_assembly_get(); + fx_iterator *it = fx_assembly_get_types(self); + fx_foreach(v, it) + { + fx_type *ty = NULL; + fx_value_get_object(&v, &ty); + const char *ty_name = fx_type_get_name(ty); + bshell_command_class *cmd = fx_type_get_interface( + ty, + BSHELL_TYPE_COMMAND); + if (!cmd || !cmd->c_get_callable_name_static) { + continue; + } + + fx_string_clear(target_name); + cmd->c_get_callable_name_static(ty, target_name); + + if (!fx_strcmp_nocase( + fx_string_get_cstr(call_name), + fx_string_get_cstr(target_name))) { + return fx_object_create(fx_type_get_id(ty)); + } + } + fx_iterator_unref(it); + + return NULL; +} + +void bshell_command_set_args( + bshell_command *cmd, + fx_value *args, + size_t nr_args) +{ + FX_CLASS_DISPATCH_STATIC_V( + BSHELL_TYPE_COMMAND, + command_set_args, + cmd, + args, + nr_args); +} + +void bshell_command_set_args_reverse( + bshell_command *cmd, + fx_value *args, + size_t nr_args) +{ + FX_CLASS_DISPATCH_STATIC_V( + BSHELL_TYPE_COMMAND, + command_set_args_reverse, + cmd, + args, + nr_args); +} + +const fx_value *bshell_command_get_arg(bshell_command *cmd, size_t index) +{ + FX_CLASS_DISPATCH_STATIC( + BSHELL_TYPE_COMMAND, + command_get_arg, + cmd, + index); +} + +enum bshell_status bshell_command_begin_processing(bshell_command *command) +{ + FX_CLASS_DISPATCH_VIRTUAL_0( + bshell_command, + BSHELL_TYPE_COMMAND, + BSHELL_ERR_NOT_SUPPORTED, + c_begin_processing, + command); +} + +enum bshell_status bshell_command_process_record( + bshell_command *command, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + FX_CLASS_DISPATCH_VIRTUAL( + bshell_command, + BSHELL_TYPE_COMMAND, + BSHELL_ERR_NOT_SUPPORTED, + c_process_record, + command, + pipeline, + rt); +} + +enum bshell_status bshell_command_end_processing(bshell_command *command) +{ + FX_CLASS_DISPATCH_VIRTUAL_0( + bshell_command, + BSHELL_TYPE_COMMAND, + BSHELL_ERR_NOT_SUPPORTED, + c_end_processing, + command); +} + +FX_TYPE_CLASS_BEGIN(bshell_command) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_PROPERTY("command_type", get_command_type, NULL); + FX_TYPE_PROPERTY("name", get_name, NULL); + FX_TYPE_PROPERTY("version", get_version, NULL); + FX_TYPE_PROPERTY("source", get_source, NULL); +FX_TYPE_CLASS_END(bshell_command) + +FX_TYPE_DEFINITION_BEGIN(bshell_command) + FX_TYPE_ID(0x5c50630d, 0x7535, 0x40ed, 0xbae5, 0x88aabc274d79); + FX_TYPE_NAME("bshell.command"); + FX_TYPE_CLASS(bshell_command_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_command_p); +FX_TYPE_DEFINITION_END(bshell_command) diff --git a/bshell/command/command.h b/bshell/command/command.h new file mode 100644 index 0000000..4195d64 --- /dev/null +++ b/bshell/command/command.h @@ -0,0 +1,62 @@ +#ifndef COMMAND_COMMAND_H_ +#define COMMAND_COMMAND_H_ + +#include +#include +#include +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_COMMAND (bshell_command_get_type()) + +FX_DECLARE_TYPE(bshell_command); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_command) + const char *c_command_type; + enum bshell_status ( + *c_get_callable_name)(const bshell_command *, fx_string *); + enum bshell_status ( + *c_get_callable_name_static)(const fx_type *, fx_string *); + enum bshell_status ( + *c_get_description)(const bshell_command *, fx_string *); + enum bshell_status ( + *c_get_description_static)(const fx_type *, fx_string *); + enum bshell_status (*c_begin_processing)(bshell_command *); + enum bshell_status (*c_process_record)( + bshell_command *, + FX_TYPE_FWDREF(bshell_pipeline) * pipeline, + struct bshell_runtime *); + enum bshell_status (*c_end_processing)(bshell_command *); +FX_TYPE_CLASS_DECLARATION_END(bshell_command) + +extern fx_type_id bshell_command_get_type(void); + +extern bshell_command *bshell_command_find_static(const char *callable_name); + +extern void bshell_command_set_args( + bshell_command *cmd, + fx_value *args, + size_t nr_args); +extern void bshell_command_set_args_reverse( + bshell_command *cmd, + fx_value *args, + size_t nr_args); +extern const fx_value *bshell_command_get_arg( + bshell_command *cmd, + size_t index); + +extern enum bshell_status bshell_command_begin_processing( + bshell_command *command); +extern enum bshell_status bshell_command_process_record( + bshell_command *command, + FX_TYPE_FWDREF(bshell_pipeline) * pipeline, + struct bshell_runtime *rt); +extern enum bshell_status bshell_command_end_processing( + bshell_command *command); + +FX_DECLS_END; + +#endif diff --git a/bshell/command/function.c b/bshell/command/function.c new file mode 100644 index 0000000..e69de29 diff --git a/bshell/command/function.h b/bshell/command/function.h new file mode 100644 index 0000000..4f9e091 --- /dev/null +++ b/bshell/command/function.h @@ -0,0 +1,23 @@ +#ifndef COMMAND_FUNCTION_H_ +#define COMMAND_FUNCTION_H_ + +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_FUNCTION (bshell_function_get_type()) + +FX_DECLARE_TYPE(bshell_function); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_function) +FX_TYPE_CLASS_DECLARATION_END(bshell_function) + +extern fx_type_id bshell_function_get_type(void); + +extern bshell_function *bshell_function_create(const char *name); + +FX_DECLS_END; + +#endif