From c7ee41af7150f0d1374e22a3539cf42faa4d1bd6 Mon Sep 17 00:00:00 2001 From: lamorim42 Date: Sat, 21 Feb 2026 20:27:01 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Init:=20iniciando=20altera=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - criação de estrutura de modulos - Inicio de funções do FS - abistração para file system - comentando a mian - projeto não funcional --- README.md | 134 ++++++++++++++++++++++++++ app/include/fs.h | 19 ++++ app/src/{ => cli}/init_and_free_cli.c | 0 app/src/{ => cli}/input_validation.c | 0 app/src/{ => cli}/is_valid_arg.c | 0 app/src/{ => cli}/is_valid_name.c | 0 app/src/{ => cli}/is_valid_option.c | 0 app/src/{ => core}/building_project.c | 0 app/src/{ => core}/write_in_files.c | 0 app/src/fs/fs_create_dir.c | 11 +++ app/src/fs/fs_create_file.c | 0 app/src/main.c | 55 +++++++---- app/src/{ => utils}/cli_utils.c | 0 13 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 app/include/fs.h rename app/src/{ => cli}/init_and_free_cli.c (100%) rename app/src/{ => cli}/input_validation.c (100%) rename app/src/{ => cli}/is_valid_arg.c (100%) rename app/src/{ => cli}/is_valid_name.c (100%) rename app/src/{ => cli}/is_valid_option.c (100%) rename app/src/{ => core}/building_project.c (100%) rename app/src/{ => core}/write_in_files.c (100%) create mode 100644 app/src/fs/fs_create_dir.c create mode 100644 app/src/fs/fs_create_file.c rename app/src/{ => utils}/cli_utils.c (100%) diff --git a/README.md b/README.md index 4f97fd4..e0b565b 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,137 @@ ft new project minishell ![giff exemplo do ft cli](./assets/ft_cli_ex.gif) + +# Prompts + +Como podemos aprimorar este projeto? Por exemplo, que arquitetura de projeto poderiamos usar? E, como otimizar o código para facilitar a inclusão de novas funcionalidades? + +Como podemos aprimorar um projeto em C que é uma CLI para criar arquivos para começar um projeto em C? Hoje o projeto executa apenas o comando `ft new project minishell` criando os arquivos: minishell/app/includes/minishell.h, minishell/app/src/main.c, minishell/.gitignore, minishell/README.md e minishell/Makefile. Por exemplo, que arquitetura de projeto poderiamos usar? E, como otimizar o código para facilitar a inclusão de novas funcionalidades? + +# Projeto refatoração + +Já tememos uma CLI que cria um template básico. Agora a evolução natural é transformar isso em uma ferramenta extensível, modular e sustentável. + +## Separação sujerida: + +| Módulo | Responsabilidade | +| --------------- | ---------------------------- | +| parser | Interpretar argv | +| dispatcher | Chamar comando correto | +| commands | Lógica específica do comando | +| file_generator | Criar diretórios e arquivos | +| template_engine | Substituir variáveis | +| fs | Abstração de filesystem | + +## Implementar `Command Pattern` em C + +Mesmo em C, podemos simular orientação a objetos usando struct + function pointers. + +**Exemplo**: + +```C +//Nova estrutura para comando +typedef struct s_command +{ + const char *name; + int (*execute)(int argc, char **argv); +} t_command; + +//Registro de comandos, uma array de comandos +t_command commands[] = { + {"new", cmd_new}, + {"init", cmd_init}, + {NULL, NULL} +}; + +//Dispatcher +int dispatch_command(const char *cmd, int argc, char **argv) +{ + for (int i = 0; commands[i].name != NULL; i++) + { + if (strcmp(commands[i].name, cmd) == 0) + return commands[i].execute(argc, argv); + } + printf("Unknown command\n"); + return 1; +} +``` + +Com isso para adicionar um novo comando fazemos: + +1- Criar arquivo em `commands/` + +2- Registrar no array + +Zero impacto no core. + +## Orientações de projeto: + +1- Uma função = uma responsabilidade + +2- Evitar funções com mais de 30 linhas + +3- Evitar dependência circular entre módulos + +4- Header minimalista + +5- Nunca incluir header desnecessário + +## Ordem para execução de tarefas: + +Etapa 1: +Refatorar para Command Pattern + separação por módulos + +getopt(), getopt_long() + +Etapa 2: +Criar sistema de templates externo + +Etapa 3: +Criar arquitetura de projeto mais robusta + +Etapa 4: +Adicionar novos comandos + +Etapa 5: +Testes + documentação + +## Sugestão de comportamento + +```markdown +sequenceDiagram + participant User + participant Main + participant Parser + participant Dispatcher + participant CmdNew + participant FileGenerator + participant TemplateEngine + participant ft_fs + + User->>Main: ft new project minishell + + Main->>Parser: parse(argc, argv) + Parser-->>Main: CLIContext { command="new", args } + + Main->>Dispatcher: dispatch(context) + + Dispatcher->>CmdNew: execute(context) + + CmdNew->>FileGenerator: create_project_structure(context) + + FileGenerator->>ft_fs: create_dir("minishell/") + FileGenerator->>ft_fs: create_dir("minishell/app/") + FileGenerator->>ft_fs: create_dir("minishell/app/src/") + FileGenerator->>ft_fs: create_dir("minishell/app/includes/") + + CmdNew->>TemplateEngine: render("main.c.tpl", context) + TemplateEngine-->>CmdNew: rendered_main.c + + CmdNew->>FileGenerator: create_file(path, rendered_main.c) + + FileGenerator->>ft_fs: write_file(path, content) + + CmdNew-->>Dispatcher: SUCCESS + Dispatcher-->>Main: EXIT_SUCCESS +``` \ No newline at end of file diff --git a/app/include/fs.h b/app/include/fs.h new file mode 100644 index 0000000..d9db96e --- /dev/null +++ b/app/include/fs.h @@ -0,0 +1,19 @@ +#ifndef FS_H +# define FS_H + +# include +# include +# include +# include +# include + +# include +# include +# include +# include + +int fs_create_dir(const char *path); +int fs_create_file(const char *path, const char *content); +// int fs_exists(const char *path); + +#endif \ No newline at end of file diff --git a/app/src/init_and_free_cli.c b/app/src/cli/init_and_free_cli.c similarity index 100% rename from app/src/init_and_free_cli.c rename to app/src/cli/init_and_free_cli.c diff --git a/app/src/input_validation.c b/app/src/cli/input_validation.c similarity index 100% rename from app/src/input_validation.c rename to app/src/cli/input_validation.c diff --git a/app/src/is_valid_arg.c b/app/src/cli/is_valid_arg.c similarity index 100% rename from app/src/is_valid_arg.c rename to app/src/cli/is_valid_arg.c diff --git a/app/src/is_valid_name.c b/app/src/cli/is_valid_name.c similarity index 100% rename from app/src/is_valid_name.c rename to app/src/cli/is_valid_name.c diff --git a/app/src/is_valid_option.c b/app/src/cli/is_valid_option.c similarity index 100% rename from app/src/is_valid_option.c rename to app/src/cli/is_valid_option.c diff --git a/app/src/building_project.c b/app/src/core/building_project.c similarity index 100% rename from app/src/building_project.c rename to app/src/core/building_project.c diff --git a/app/src/write_in_files.c b/app/src/core/write_in_files.c similarity index 100% rename from app/src/write_in_files.c rename to app/src/core/write_in_files.c diff --git a/app/src/fs/fs_create_dir.c b/app/src/fs/fs_create_dir.c new file mode 100644 index 0000000..929e1f7 --- /dev/null +++ b/app/src/fs/fs_create_dir.c @@ -0,0 +1,11 @@ +#include + +int fs_create_dir(const char *path) +{ + if (mkdir(path, 0755) == -1) + { + perror("Error creating directory"); + return -1; + } + return 0; +} \ No newline at end of file diff --git a/app/src/fs/fs_create_file.c b/app/src/fs/fs_create_file.c new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main.c b/app/src/main.c index 207d166..ad236d2 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -6,23 +6,40 @@ static void fallback_msg(void) dprintf(STDERR_FILENO, FALLBACK); } -int main(int argc, char **argv) +// int main(int argc, char **argv) +// { +// t_cli data; + +// if (argc == 2 && !strcmp(argv[1], "--help")) +// { +// dprintf(1, HELP); +// return (0); +// } +// if (argc != 4) +// return (fallback_msg() , 1); +// init_cli(&data, argc, argv); +// if (is_valid_input(&data)) +// { +// building_project(&data); +// write_in_files(&data); +// } +// free_cli(&data); +// return (0); +// } + +typedef struct s_command { - t_cli data; - - if (argc == 2 && !strcmp(argv[1], "--help")) - { - dprintf(1, HELP); - return (0); - } - if (argc != 4) - return (fallback_msg() , 1); - init_cli(&data, argc, argv); - if (is_valid_input(&data)) - { - building_project(&data); - write_in_files(&data); - } - free_cli(&data); - return (0); -} + const char *name; + int (*execute)(int argc, char **argv); +} t_command; + +t_command commands[] = { + {"new", cmd_new}, + {"init", cmd_init}, + {NULL, NULL} +}; + + int main(int argc, char **argv) + { + + } diff --git a/app/src/cli_utils.c b/app/src/utils/cli_utils.c similarity index 100% rename from app/src/cli_utils.c rename to app/src/utils/cli_utils.c From 7c4ef323b52ea7b295258b9b4243b95af372b643 Mon Sep 17 00:00:00 2001 From: lamorim42 Date: Wed, 25 Feb 2026 16:27:06 -0300 Subject: [PATCH 2/3] REFACT: adicionando command pathern e modulos --- Makefile | 53 ++++---- app/include/command.h | 14 +++ app/include/ft_cli.h | 22 ++++ app/src/commands/cmd_new.c | 214 ++++++++++++++++++++++++++++++++ app/src/dispatcher/dispatcher.c | 16 +++ app/src/fs/fs_create_file.c | 13 ++ app/src/main.c | 28 ++--- app/src/parser/parser.c | 14 +++ templates/create_structure.sh | 83 +++++++++++++ templates/default/include.tpl | 11 ++ templates/default/main.tpl | 7 ++ templates/default/makefile.tpl | 41 ++++++ templates/default/readme.tpl | 1 + 13 files changed, 476 insertions(+), 41 deletions(-) create mode 100644 app/include/command.h create mode 100644 app/src/commands/cmd_new.c create mode 100644 app/src/dispatcher/dispatcher.c create mode 100644 app/src/parser/parser.c create mode 100755 templates/create_structure.sh create mode 100644 templates/default/include.tpl create mode 100644 templates/default/main.tpl create mode 100644 templates/default/makefile.tpl create mode 100644 templates/default/readme.tpl diff --git a/Makefile b/Makefile index b7ee761..dbe9ad8 100644 --- a/Makefile +++ b/Makefile @@ -2,21 +2,27 @@ NAME = ft CC = clang CFLAGS = -Wall -Werror -Wextra -g -INCLUDE = -I ./app/include - -SRC = main.c -SRC += init_and_free_cli.c -SRC += is_valid_option.c -SRC += is_valid_arg.c -SRC += is_valid_name.c -SRC += input_validation.c -SRC += building_project.c -SRC += cli_utils.c -SRC += write_in_files.c - -OBJS = $(SRC:.c=.o) -OBJ = $(addprefix ./app/obj/, $(OBJS)) -OBJ_DIR = ./app/obj +INCLUDE = -I app/include + +# Fontes organizados por subdiretórios +SRC = \ + app/src/main.c \ + app/src/cli/init_and_free_cli.c \ + app/src/cli/is_valid_option.c \ + app/src/cli/is_valid_arg.c \ + app/src/cli/is_valid_name.c \ + app/src/cli/input_validation.c \ + app/src/core/building_project.c \ + app/src/core/write_in_files.c \ + app/src/utils/cli_utils.c \ + app/src/commands/cmd_new.c \ + app/src/dispatcher/dispatcher.c \ + app/src/parser/parser.c \ +# app/src/fs/fs_create_dir.c \ +# app/src/fs/fs_create_file.c + +OBJ_DIR = app/obj +OBJS = $(SRC:app/src/%.c=$(OBJ_DIR)/%.o) RM = rm -rf @@ -24,16 +30,19 @@ VPATH = ./app/src LOCAL_INSTALL = /usr/local/bin + +# Garante que o subdiretório de destino existe antes de compilar $(OBJ_DIR)/%.o: %.c - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ all: obj_dir $(NAME) -$(NAME): $(OBJ) - $(CC) $(CFLAGS) $(OBJ) $(LIBFT) -o $(NAME) +$(NAME): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $(NAME) clean: - $(RM) $(OBJ) + $(RM) $(OBJS) $(RM) $(OBJ_DIR) fclean: clean @@ -45,10 +54,6 @@ install: $(NAME) re: fclean all obj_dir: - @if [ ! -d "$(OBJ_DIR)" ]; then\ - mkdir $(OBJ_DIR);\ - else\ - echo "make: Nothing to be done for 'all'.";\ - fi + @mkdir -p $(OBJ_DIR) .PHONY= all clean fclean re install $(NAME) diff --git a/app/include/command.h b/app/include/command.h new file mode 100644 index 0000000..f6beee8 --- /dev/null +++ b/app/include/command.h @@ -0,0 +1,14 @@ +#ifndef COMMAND_H +# define COMMAND_H + +# include +# include "ft_cli.h" + +int cmd_new_execute(t_cli_context *ctx); + +t_command g_commands[] = { + {"new", cmd_new_execute}, + {NULL, NULL} +}; + +#endif \ No newline at end of file diff --git a/app/include/ft_cli.h b/app/include/ft_cli.h index 9cbc1ba..7e854f6 100644 --- a/app/include/ft_cli.h +++ b/app/include/ft_cli.h @@ -24,6 +24,24 @@ typedef struct s_cli char *include; } t_cli; +typedef struct s_cli_context +{ + char *command; + char *type; + char *project_name; + int verbose; +} t_cli_context; + +typedef int (*t_command_fn)(t_cli_context *ctx); + +typedef struct s_command +{ + const char *name; + t_command_fn execute; +} t_command; + + + void init_cli(t_cli *data, int argc, char **argv); void free_cli(t_cli *data); int is_valid_option(t_cli *data); @@ -38,4 +56,8 @@ void write_in_files(t_cli *data); char *join(char *s1, char *s2); char *strmapi(char const *s, int (*f)(int)); + +int parse_args(int argc, char **argv, t_cli_context *ctx); +int dispatch_command(t_cli_context *ctx); + #endif diff --git a/app/src/commands/cmd_new.c b/app/src/commands/cmd_new.c new file mode 100644 index 0000000..7f08852 --- /dev/null +++ b/app/src/commands/cmd_new.c @@ -0,0 +1,214 @@ +#include "../../include/ft_cli.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// Função para converter string para maiúscula +static char *str_to_upper(const char *str) +{ + char *result = malloc(strlen(str) + 1); + if (!result) + return NULL; + for (int i = 0; str[i]; i++) + result[i] = toupper((unsigned char)str[i]); + result[strlen(str)] = '\0'; + return result; +} + +// Função para criar um diretório +static int create_directory(const char *path) +{ + struct stat st; + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + return 0; + return mkdir(path, 0775); +} + +// Função para ler um arquivo inteiro +static char *read_file(const char *path) +{ + FILE *file = fopen(path, "r"); + if (!file) + return NULL; + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + char *content = malloc(size + 1); + if (!content) { + fclose(file); + return NULL; + } + + fread(content, 1, size, file); + content[size] = '\0'; + fclose(file); + return content; +} + +// Função para substituir placeholders em um string +static char *replace_placeholder(const char *content, const char *placeholder, const char *replacement) +{ + size_t content_len = strlen(content); + size_t placeholder_len = strlen(placeholder); + size_t replacement_len = strlen(replacement); + + // Contar ocorrências + int count = 0; + const char *temp = content; + while ((temp = strstr(temp, placeholder)) != NULL) { + count++; + temp += placeholder_len; + } + + if (count == 0) + return strdup(content); + + // Alocar espaço para o novo conteúdo + size_t new_size = content_len + count * (replacement_len - placeholder_len) + 1; + char *result = malloc(new_size); + if (!result) + return NULL; + + // Fazer as substituições + const char *src = content; + char *dst = result; + const char *found; + + while ((found = strstr(src, placeholder)) != NULL) { + // Copiar tudo até o placeholder + size_t len = found - src; + strncpy(dst, src, len); + dst += len; + + // Copiar o replacement + strcpy(dst, replacement); + dst += replacement_len; + + // Avançar src + src = found + placeholder_len; + } + + // Copiar o resto + strcpy(dst, src); + return result; +} + +// Função para processar e escrever um template +static int process_template(const char *template_path, const char *output_path, + const char *project_name, const char *include_guard) +{ + char *content = read_file(template_path); + if (!content) { + fprintf(stderr, "Erro ao ler template: %s\n", template_path); + return 1; + } + + // Fazer substituições + char *temp = replace_placeholder(content, "{{PROJECT_NAME}}", project_name); + free(content); + + char *final = replace_placeholder(temp, "{{INCLUDE_NAME}}", include_guard); + free(temp); + + // Escrever arquivo + FILE *out = fopen(output_path, "w"); + if (!out) { + fprintf(stderr, "Erro ao criar arquivo: %s\n", output_path); + free(final); + return 1; + } + + fputs(final, out); + fclose(out); + free(final); + + printf("Criado arquivo: %s\n", output_path); + return 0; +} + +int cmd_new_execute(t_cli_context *ctx) +{ + printf("Executando comando NEW para projeto %s\n", ctx->project_name); + + // Obter caminho do executável + char exe_path[512]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len == -1) { + fprintf(stderr, "Erro ao obter caminho do executável\n"); + return 1; + } + exe_path[len] = '\0'; + char *exe_dir = dirname(exe_path); + + // Construir caminhos dos templates + char include_tpl[512], main_tpl[512], makefile_tpl[512], readme_tpl[512]; + snprintf(include_tpl, sizeof(include_tpl), "%s/templates/default/include.tpl", exe_dir); + snprintf(main_tpl, sizeof(main_tpl), "%s/templates/default/main.tpl", exe_dir); + snprintf(makefile_tpl, sizeof(makefile_tpl), "%s/templates/default/makefile.tpl", exe_dir); + snprintf(readme_tpl, sizeof(readme_tpl), "%s/templates/default/readme.tpl", exe_dir); + + // Preparar nomes + char *project_upper = str_to_upper(ctx->project_name); + if (!project_upper) + return 1; + + char include_guard[256]; + snprintf(include_guard, sizeof(include_guard), "%s_H", project_upper); + free(project_upper); + + // Criar diretórios + create_directory(ctx->project_name); + create_directory("app"); + + char buf[512]; + snprintf(buf, sizeof(buf), "%s/app", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/app/includes", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/app/src", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/app/obj", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/app/tests", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/app/bin", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/doc", ctx->project_name); + create_directory(buf); + + snprintf(buf, sizeof(buf), "%s/doc/assets", ctx->project_name); + create_directory(buf); + + // Processar templates + snprintf(buf, sizeof(buf), "%s/app/includes/%s.h", ctx->project_name, ctx->project_name); + if (process_template(include_tpl, buf, ctx->project_name, include_guard) != 0) + return 1; + + snprintf(buf, sizeof(buf), "%s/app/src/main.c", ctx->project_name); + if (process_template(main_tpl, buf, ctx->project_name, include_guard) != 0) + return 1; + + snprintf(buf, sizeof(buf), "%s/Makefile", ctx->project_name); + if (process_template(makefile_tpl, buf, ctx->project_name, include_guard) != 0) + return 1; + + snprintf(buf, sizeof(buf), "%s/README.md", ctx->project_name); + if (process_template(readme_tpl, buf, ctx->project_name, include_guard) != 0) + return 1; + + printf("Estrutura criada com sucesso em %s!\n", ctx->project_name); + return 0; +} \ No newline at end of file diff --git a/app/src/dispatcher/dispatcher.c b/app/src/dispatcher/dispatcher.c new file mode 100644 index 0000000..9c256dd --- /dev/null +++ b/app/src/dispatcher/dispatcher.c @@ -0,0 +1,16 @@ +#include "ft_cli.h" +#include "command.h" +#include +#include + +int dispatch_command(t_cli_context *ctx) +{ + for (int i = 0; g_commands[i].name != NULL; i++) + { + if (strcmp(g_commands[i].name, ctx->command) == 0) + return g_commands[i].execute(ctx); + } + + printf("Comando desconhecido: %s\n", ctx->command); + return 1; +} \ No newline at end of file diff --git a/app/src/fs/fs_create_file.c b/app/src/fs/fs_create_file.c index e69de29..b951a95 100644 --- a/app/src/fs/fs_create_file.c +++ b/app/src/fs/fs_create_file.c @@ -0,0 +1,13 @@ +#include + +int fs_create_file(const char *path) +{ + FILE *file = fopen(path, "w"); + if (file == NULL) + { + perror("Error creating file"); + return -1; + } + fclose(file); + return 0; +} \ No newline at end of file diff --git a/app/src/main.c b/app/src/main.c index ad236d2..998f035 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -1,10 +1,10 @@ #include "ft_cli.h" -static void fallback_msg(void) -{ - dprintf(STDERR_FILENO, FALLBACK); -} +// static void fallback_msg(void) +// { +// dprintf(STDERR_FILENO, FALLBACK); +// } // int main(int argc, char **argv) // { @@ -27,19 +27,13 @@ static void fallback_msg(void) // return (0); // } -typedef struct s_command -{ - const char *name; - int (*execute)(int argc, char **argv); -} t_command; -t_command commands[] = { - {"new", cmd_new}, - {"init", cmd_init}, - {NULL, NULL} -}; +int main(int argc, char **argv) +{ + t_cli_context ctx; - int main(int argc, char **argv) - { + if (parse_args(argc, argv, &ctx)) + return 1; - } + return dispatch_command(&ctx); +} \ No newline at end of file diff --git a/app/src/parser/parser.c b/app/src/parser/parser.c new file mode 100644 index 0000000..9a6c61e --- /dev/null +++ b/app/src/parser/parser.c @@ -0,0 +1,14 @@ +#include "ft_cli.h" + +int parse_args(int argc, char **argv, t_cli_context *ctx) +{ + if (argc != 4) + return 1; + + ctx->command = argv[1]; + ctx->type = argv[2]; + ctx->project_name = argv[3]; + ctx->verbose = 0; + + return 0; +} \ No newline at end of file diff --git a/templates/create_structure.sh b/templates/create_structure.sh new file mode 100755 index 0000000..68c8d84 --- /dev/null +++ b/templates/create_structure.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Descobre o diretório do script +SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" + +#Estrutura do projeto +APP_DIR="app" +INCLUDES_DIR="$APP_DIR/includes" +SRC_DIR="$APP_DIR/src" +OBJS_DIR="$APP_DIR/obj" +TEST_DIR="$APP_DIR/tests" +BIN_DIR="$APP_DIR/bin" +DOC_DIR="doc" +ASSETS_DIR="$DOC_DIR/assets" + +#detro da includes vamos colocar o include.h +#dentro do src vamos colocar a main.c +#a pasta objs será utilizada no makefile para armazenar os arquivos objeto +#a pasta bin será utilizada para armazenar o arquivo executável gerado no makefile +#O arquivo README.md será criado na raiz, assim como o .gitignore e o Makefile + +#verificar se o nome do projeto foi fornecido como único argumento +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +PROJECT_NAME=$1 # Verificar se o nome do projeto foi fornecido +if [ -z "$PROJECT_NAME" ]; then + echo "Usage: $0 " + exit 1 +fi + +#Project name em maiúsculo para o header guard +PROJECT_NAME_UPPER=$(echo "$PROJECT_NAME" | tr '[:lower:]' '[:upper:]') + +#Nome das variáveis para o template do header +INCLUDE_NAME="$PROJECT_NAME_UPPER"_H + +#inclunir uma validação para verificar se o pasta app/includes existe, caso contrário criar a pasta +if [ ! -d "$INCLUDES_DIR" ]; then + mkdir -p "$INCLUDES_DIR" +fi + +if [ ! -d "$SRC_DIR" ]; then + mkdir -p "$SRC_DIR" +fi + +if [ ! -d "$OBJS_DIR" ]; then + mkdir -p "$OBJS_DIR" +fi + +if [ ! -d "$TEST_DIR" ]; then + mkdir -p "$TEST_DIR" +fi + +if [ ! -d "$BIN_DIR" ]; then + mkdir -p "$BIN_DIR" +fi + +if [ ! -d "$DOC_DIR" ]; then + mkdir -p "$DOC_DIR" +fi + +if [ ! -d "$ASSETS_DIR" ]; then + mkdir -p "$ASSETS_DIR" +fi + +sed -e "s/{{INCLUDE_NAME}}/$INCLUDE_NAME/g" \ + -e "s/{{FUNC_NAME}}/$FUNC_NAME/g" "$SCRIPT_DIR/default/include.tpl" \ + > app/includes/$PROJECT_NAME.h + +sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ + "$SCRIPT_DIR/default/main.tpl" \ + > app/src/main.c + +sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ + "$SCRIPT_DIR/default/makefile.tpl" \ + > Makefile + +sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ + "$SCRIPT_DIR/default/readme.tpl" \ + > README.md \ No newline at end of file diff --git a/templates/default/include.tpl b/templates/default/include.tpl new file mode 100644 index 0000000..f960ae6 --- /dev/null +++ b/templates/default/include.tpl @@ -0,0 +1,11 @@ +#ifndef {{INCLUDE_NAME}} +# define {{INCLUDE_NAME}} + +//Includes +# include + +//Estruturas + +//Funções + +#endif // {{INCLUDE_NAME}} \ No newline at end of file diff --git a/templates/default/main.tpl b/templates/default/main.tpl new file mode 100644 index 0000000..8aabc8b --- /dev/null +++ b/templates/default/main.tpl @@ -0,0 +1,7 @@ +#include "{{PROJECT_NAME}}.h" + +int main(void) +{ + printf("Hello {{PROJECT_NAME}}!\n"); + return (0); +} \ No newline at end of file diff --git a/templates/default/makefile.tpl b/templates/default/makefile.tpl new file mode 100644 index 0000000..f068c29 --- /dev/null +++ b/templates/default/makefile.tpl @@ -0,0 +1,41 @@ +NAME = {{PROJECT_NAME}} +CC = clang +CFLAGS = -Wall -Werror -Wextra -g +INCLUDE = -I ./app/includes + +SRC = main.c + +OBJS = $(notdir $(SRC:.c=.o)) +OBJ = $(addprefix ./app/obj/, $(OBJS)) +OBJ_DIR = ./app/obj + +RM = rm -rf + +VPATH = ./app/src + +$(OBJ_DIR)/%.o: app/src/%.c + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +all: obj_dir $(NAME) + +$(NAME): $(OBJ) + $(CC) $(CFLAGS) $(OBJ) $(LIBFT) -o app/bin/$(NAME) + +clean: + $(RM) $(OBJ) + +fclean: + $(RM) $(OBJ) + $(RM) $(OBJ_DIR) + $(RM) $(NAME) + +re: fclean all + +obj_dir: + @if [ ! -d "$(OBJ_DIR)" ]; then\ + mkdir $(OBJ_DIR);\ + else\ + echo "make: Nothing to be done for 'all'.";\ + fi + +.PHONY= all clean fclean re $(NAME) \ No newline at end of file diff --git a/templates/default/readme.tpl b/templates/default/readme.tpl new file mode 100644 index 0000000..66927e1 --- /dev/null +++ b/templates/default/readme.tpl @@ -0,0 +1 @@ +# Hello {{PROJECT_NAME}} From 85bce6cffd23ed2a674eb1f72d914b8663350e52 Mon Sep 17 00:00:00 2001 From: lamorim42 Date: Wed, 25 Feb 2026 17:48:13 -0300 Subject: [PATCH 3/3] REFACT: refatorando fluxo --- Makefile | 6 +- app/include/fs.h | 3 +- app/src/commands/cmd_new.c | 148 ++++++++---------- app/src/fs/fs_read_file.c | 24 +++ app/src/main.c | 10 +- .../default/include.tpl | 0 {templates => ft_templates}/default/main.tpl | 0 .../default/makefile.tpl | 2 + .../default/readme.tpl | 0 templates/create_structure.sh | 83 ---------- 10 files changed, 103 insertions(+), 173 deletions(-) create mode 100644 app/src/fs/fs_read_file.c rename {templates => ft_templates}/default/include.tpl (100%) rename {templates => ft_templates}/default/main.tpl (100%) rename {templates => ft_templates}/default/makefile.tpl (92%) rename {templates => ft_templates}/default/readme.tpl (100%) delete mode 100755 templates/create_structure.sh diff --git a/Makefile b/Makefile index dbe9ad8..2d27cf7 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,9 @@ SRC = \ app/src/commands/cmd_new.c \ app/src/dispatcher/dispatcher.c \ app/src/parser/parser.c \ -# app/src/fs/fs_create_dir.c \ -# app/src/fs/fs_create_file.c + app/src/fs/fs_create_dir.c \ + app/src/fs/fs_create_file.c \ + app/src/fs/fs_read_file.c OBJ_DIR = app/obj OBJS = $(SRC:app/src/%.c=$(OBJ_DIR)/%.o) @@ -50,6 +51,7 @@ fclean: clean install: $(NAME) cp $(NAME) $(LOCAL_INSTALL) + cp ./ft_templates/ $(LOCAL_INSTALL) -r re: fclean all diff --git a/app/include/fs.h b/app/include/fs.h index d9db96e..2417504 100644 --- a/app/include/fs.h +++ b/app/include/fs.h @@ -13,7 +13,8 @@ # include int fs_create_dir(const char *path); -int fs_create_file(const char *path, const char *content); +int fs_create_file(const char *path); +char *fs_read_file(const char *path); // int fs_exists(const char *path); #endif \ No newline at end of file diff --git a/app/src/commands/cmd_new.c b/app/src/commands/cmd_new.c index 7f08852..4f48243 100644 --- a/app/src/commands/cmd_new.c +++ b/app/src/commands/cmd_new.c @@ -1,4 +1,5 @@ #include "../../include/ft_cli.h" +#include "../../include/fs.h" #include #include #include @@ -20,38 +21,6 @@ static char *str_to_upper(const char *str) return result; } -// Função para criar um diretório -static int create_directory(const char *path) -{ - struct stat st; - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) - return 0; - return mkdir(path, 0775); -} - -// Função para ler um arquivo inteiro -static char *read_file(const char *path) -{ - FILE *file = fopen(path, "r"); - if (!file) - return NULL; - - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); - - char *content = malloc(size + 1); - if (!content) { - fclose(file); - return NULL; - } - - fread(content, 1, size, file); - content[size] = '\0'; - fclose(file); - return content; -} - // Função para substituir placeholders em um string static char *replace_placeholder(const char *content, const char *placeholder, const char *replacement) { @@ -100,11 +69,19 @@ static char *replace_placeholder(const char *content, const char *placeholder, c return result; } +// Função para criar um diretório dentro do projeto +static void create_project_dir(const char *project_name, const char *subdir) +{ + char path[512]; + snprintf(path, sizeof(path), "%s/%s", project_name, subdir); + fs_create_dir(path); +} + // Função para processar e escrever um template static int process_template(const char *template_path, const char *output_path, const char *project_name, const char *include_guard) { - char *content = read_file(template_path); + char *content = fs_read_file(template_path); if (!content) { fprintf(stderr, "Erro ao ler template: %s\n", template_path); return 1; @@ -133,6 +110,44 @@ static int process_template(const char *template_path, const char *output_path, return 0; } +// Função para processar múltiplos templates +static int process_templates(const char *exe_dir, const char *project_name, + const char *include_guard) +{ + struct { + const char *tpl_file; + const char *output_format; + } templates[] = { + {"include.tpl", "%s/app/includes/%s.h"}, + {"main.tpl", "%s/app/src/main.c"}, + {"makefile.tpl", "%s/Makefile"}, + {"readme.tpl", "%s/README.md"}, + {NULL, NULL} + }; + + for (int i = 0; templates[i].tpl_file; i++) { + char tpl_path[512]; + char output_path[512]; + + snprintf(tpl_path, sizeof(tpl_path), "%s/ft_templates/default/%s", + exe_dir, templates[i].tpl_file); + + // Tratamento especial para o header + if (strstr(templates[i].output_format, ".h")) { + snprintf(output_path, sizeof(output_path), templates[i].output_format, + project_name, project_name); + } else { + snprintf(output_path, sizeof(output_path), templates[i].output_format, + project_name); + } + + if (process_template(tpl_path, output_path, project_name, include_guard) != 0) + return 1; + } + + return 0; +} + int cmd_new_execute(t_cli_context *ctx) { printf("Executando comando NEW para projeto %s\n", ctx->project_name); @@ -147,13 +162,6 @@ int cmd_new_execute(t_cli_context *ctx) exe_path[len] = '\0'; char *exe_dir = dirname(exe_path); - // Construir caminhos dos templates - char include_tpl[512], main_tpl[512], makefile_tpl[512], readme_tpl[512]; - snprintf(include_tpl, sizeof(include_tpl), "%s/templates/default/include.tpl", exe_dir); - snprintf(main_tpl, sizeof(main_tpl), "%s/templates/default/main.tpl", exe_dir); - snprintf(makefile_tpl, sizeof(makefile_tpl), "%s/templates/default/makefile.tpl", exe_dir); - snprintf(readme_tpl, sizeof(readme_tpl), "%s/templates/default/readme.tpl", exe_dir); - // Preparar nomes char *project_upper = str_to_upper(ctx->project_name); if (!project_upper) @@ -163,50 +171,26 @@ int cmd_new_execute(t_cli_context *ctx) snprintf(include_guard, sizeof(include_guard), "%s_H", project_upper); free(project_upper); - // Criar diretórios - create_directory(ctx->project_name); - create_directory("app"); - - char buf[512]; - snprintf(buf, sizeof(buf), "%s/app", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/app/includes", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/app/src", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/app/obj", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/app/tests", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/app/bin", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/doc", ctx->project_name); - create_directory(buf); - - snprintf(buf, sizeof(buf), "%s/doc/assets", ctx->project_name); - create_directory(buf); + // Criar diretórios principais + const char *dirs[] = { + "app", + "app/includes", + "app/src", + "app/obj", + "app/tests", + "app/bin", + "doc", + "doc/assets", + NULL + }; + + fs_create_dir(ctx->project_name); + for (int i = 0; dirs[i]; i++) { + create_project_dir(ctx->project_name, dirs[i]); + } // Processar templates - snprintf(buf, sizeof(buf), "%s/app/includes/%s.h", ctx->project_name, ctx->project_name); - if (process_template(include_tpl, buf, ctx->project_name, include_guard) != 0) - return 1; - - snprintf(buf, sizeof(buf), "%s/app/src/main.c", ctx->project_name); - if (process_template(main_tpl, buf, ctx->project_name, include_guard) != 0) - return 1; - - snprintf(buf, sizeof(buf), "%s/Makefile", ctx->project_name); - if (process_template(makefile_tpl, buf, ctx->project_name, include_guard) != 0) - return 1; - - snprintf(buf, sizeof(buf), "%s/README.md", ctx->project_name); - if (process_template(readme_tpl, buf, ctx->project_name, include_guard) != 0) + if (process_templates(exe_dir, ctx->project_name, include_guard) != 0) return 1; printf("Estrutura criada com sucesso em %s!\n", ctx->project_name); diff --git a/app/src/fs/fs_read_file.c b/app/src/fs/fs_read_file.c new file mode 100644 index 0000000..756b474 --- /dev/null +++ b/app/src/fs/fs_read_file.c @@ -0,0 +1,24 @@ +#include + +// Função para ler um arquivo inteiro +char *fs_read_file(const char *path) +{ + FILE *file = fopen(path, "r"); + if (!file) + return NULL; + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + char *content = malloc(size + 1); + if (!content) { + fclose(file); + return NULL; + } + + fread(content, 1, size, file); + content[size] = '\0'; + fclose(file); + return content; +} \ No newline at end of file diff --git a/app/src/main.c b/app/src/main.c index 998f035..8ff8621 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -1,10 +1,10 @@ #include "ft_cli.h" -// static void fallback_msg(void) -// { -// dprintf(STDERR_FILENO, FALLBACK); -// } +static void fallback_msg(void) +{ + dprintf(STDERR_FILENO, FALLBACK); +} // int main(int argc, char **argv) // { @@ -33,7 +33,7 @@ int main(int argc, char **argv) t_cli_context ctx; if (parse_args(argc, argv, &ctx)) - return 1; + return (fallback_msg() , 1); return dispatch_command(&ctx); } \ No newline at end of file diff --git a/templates/default/include.tpl b/ft_templates/default/include.tpl similarity index 100% rename from templates/default/include.tpl rename to ft_templates/default/include.tpl diff --git a/templates/default/main.tpl b/ft_templates/default/main.tpl similarity index 100% rename from templates/default/main.tpl rename to ft_templates/default/main.tpl diff --git a/templates/default/makefile.tpl b/ft_templates/default/makefile.tpl similarity index 92% rename from templates/default/makefile.tpl rename to ft_templates/default/makefile.tpl index f068c29..5a59f03 100644 --- a/templates/default/makefile.tpl +++ b/ft_templates/default/makefile.tpl @@ -20,6 +20,7 @@ all: obj_dir $(NAME) $(NAME): $(OBJ) $(CC) $(CFLAGS) $(OBJ) $(LIBFT) -o app/bin/$(NAME) + cp app/bin/$(NAME) ./$(NAME) clean: $(RM) $(OBJ) @@ -27,6 +28,7 @@ clean: fclean: $(RM) $(OBJ) $(RM) $(OBJ_DIR) + $(RM) app/bin/$(NAME) $(RM) $(NAME) re: fclean all diff --git a/templates/default/readme.tpl b/ft_templates/default/readme.tpl similarity index 100% rename from templates/default/readme.tpl rename to ft_templates/default/readme.tpl diff --git a/templates/create_structure.sh b/templates/create_structure.sh deleted file mode 100755 index 68c8d84..0000000 --- a/templates/create_structure.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# Descobre o diretório do script -SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" - -#Estrutura do projeto -APP_DIR="app" -INCLUDES_DIR="$APP_DIR/includes" -SRC_DIR="$APP_DIR/src" -OBJS_DIR="$APP_DIR/obj" -TEST_DIR="$APP_DIR/tests" -BIN_DIR="$APP_DIR/bin" -DOC_DIR="doc" -ASSETS_DIR="$DOC_DIR/assets" - -#detro da includes vamos colocar o include.h -#dentro do src vamos colocar a main.c -#a pasta objs será utilizada no makefile para armazenar os arquivos objeto -#a pasta bin será utilizada para armazenar o arquivo executável gerado no makefile -#O arquivo README.md será criado na raiz, assim como o .gitignore e o Makefile - -#verificar se o nome do projeto foi fornecido como único argumento -if [ $# -ne 1 ]; then - echo "Usage: $0 " - exit 1 -fi - -PROJECT_NAME=$1 # Verificar se o nome do projeto foi fornecido -if [ -z "$PROJECT_NAME" ]; then - echo "Usage: $0 " - exit 1 -fi - -#Project name em maiúsculo para o header guard -PROJECT_NAME_UPPER=$(echo "$PROJECT_NAME" | tr '[:lower:]' '[:upper:]') - -#Nome das variáveis para o template do header -INCLUDE_NAME="$PROJECT_NAME_UPPER"_H - -#inclunir uma validação para verificar se o pasta app/includes existe, caso contrário criar a pasta -if [ ! -d "$INCLUDES_DIR" ]; then - mkdir -p "$INCLUDES_DIR" -fi - -if [ ! -d "$SRC_DIR" ]; then - mkdir -p "$SRC_DIR" -fi - -if [ ! -d "$OBJS_DIR" ]; then - mkdir -p "$OBJS_DIR" -fi - -if [ ! -d "$TEST_DIR" ]; then - mkdir -p "$TEST_DIR" -fi - -if [ ! -d "$BIN_DIR" ]; then - mkdir -p "$BIN_DIR" -fi - -if [ ! -d "$DOC_DIR" ]; then - mkdir -p "$DOC_DIR" -fi - -if [ ! -d "$ASSETS_DIR" ]; then - mkdir -p "$ASSETS_DIR" -fi - -sed -e "s/{{INCLUDE_NAME}}/$INCLUDE_NAME/g" \ - -e "s/{{FUNC_NAME}}/$FUNC_NAME/g" "$SCRIPT_DIR/default/include.tpl" \ - > app/includes/$PROJECT_NAME.h - -sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ - "$SCRIPT_DIR/default/main.tpl" \ - > app/src/main.c - -sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ - "$SCRIPT_DIR/default/makefile.tpl" \ - > Makefile - -sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \ - "$SCRIPT_DIR/default/readme.tpl" \ - > README.md \ No newline at end of file