commit 51958d83109bff3fec9843106bce28fc5ceb827a Author: gmega Date: Fri Jan 30 10:30:16 2026 -0300 initial import diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a8207b4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,58 @@ +# Generated from CLion C/C++ Code Style settings +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignOperands: true +AlignTrailingComments: false +AlwaysBreakTemplateDeclarations: Yes +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ContinuationIndentWidth: 8 +IncludeCategories: + - Regex: '^<.*' + Priority: 1 + - Regex: '^".*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentWidth: 4 +InsertNewlineAtEOF: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +TabWidth: 4 +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ffe7e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmake-* +.idea \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..53a20af --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,28 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +storageconsole is a C11 interactive CLI application for managing a distributed storage node. It wraps an external `libstorage` library (Nim-based) through a command dispatch console. + +## Build + +Requires the `LOGOS_STORAGE_NIM_ROOT` environment variable pointing to the external storage library root (contains `library/libstorage.h` and platform-specific shared library). + +```sh +cmake -B build -S . +cmake --build build +``` + +The build output is an executable named `storageconsole`. + +## Architecture + +- **main.c** — Entry point and interactive console. Implements a command dispatch loop using a static table of `command` structs mapping names to function pointers. The `console` struct holds an opaque `void *ctx` pointer to the storage node instance. +- **easylibstorage.h** — High-level wrapper API declarations over `libstorage`. Defines `STORAGE_NODE` (opaque pointer), `node_config`, `progress_callback`, and functions for node lifecycle (`e_storage_new/start/stop/destroy`) and data operations (`e_storage_upload/download`). These are declared but not all wired into console commands yet. +- **External dependency**: `libstorage` (platform-specific `.so`/`.dylib`/`.dll`) found via `LOGOS_STORAGE_NIM_ROOT`. Currently only `sync_start()` from `libstorage.h` is called directly. + +## Code Style + +Formatting is enforced via `.clang-format` (LLVM-based, 4-space indent, 120-char column limit). diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7960985 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.14) +project(storageconsole C) +set(CMAKE_C_STANDARD 11) + +add_executable(storageconsole main.c + easylibstorage.h +) + +if(NOT DEFINED LOGOS_STORAGE_NIM_ROOT) + message(FATAL_ERROR "Need to set LOGOS_STORAGE_NIM_ROOT") +endif() + +target_include_directories(storageconsole PRIVATE "${LOGOS_STORAGE_NIM_ROOT}/library") + +if(APPLE) + set(LIBSTORAGE_NAMES libstorage.dylib) +elseif(WIN32) + set(LIBSTORAGE_NAMES libstorage.dll) +else() + set(LIBSTORAGE_NAMES libstorage.so) +endif() + +find_library(LIBSTORAGE_PATH NAMES ${LIBSTORAGE_NAMES} PATHS ${LOGOS_STORAGE_NIM_ROOT}/build NO_DEFAULT_PATH) + +if(LIBSTORAGE_PATH) + target_link_libraries(storageconsole PRIVATE ${LIBSTORAGE_PATH}) +else() + message(WARNING "libstorage not found. Build or provide it before linking.") +endif() + diff --git a/PROMPT.md b/PROMPT.md new file mode 100644 index 0000000..f125ef5 --- /dev/null +++ b/PROMPT.md @@ -0,0 +1,32 @@ +I want you to implement the API described in easylibstorage.h in a file name easylibstorage.c. This is a simplified +wrapper on top of libstorage. You can find the headers for libstorage here: + +- /home/giuliano/Work/Status/logos-storage-nim/library/libstorage.h + +You can find examples of how to use libstorage here: + +- /home/giuliano/Work/Status/logos-storage-nim/examples/c/examples.c + +I want you to implement one function at a time. You MUST follow a Test Driven Development approach where you: + +1. write a test for a function; (red) +2. write the function; +3. run it until your test is green; (green) +4. stop, think of ways to refactor and simplify the code, and do it; (refactor) +5. once you're done refactoring and the tests are green, move to the next function. This step is VERY IMPORTANT. You MUST look for ways to simplify, +deduplicate, and coalesce code here, WITHOUT OVERCOMPLICATING. SIMPLICITY +IS KEY AND YOUR GUIDING PRINCIPLE. + +Feel free to use an idiomatic C way to write your unit tests, but DO NOT implement the next function before doing +the full 1-5 cycle first. You need to follow the red-greed-refactor tenets of TDD. + +Finally, I want you to wire all of the above in the console application under main.c. This console application must +support the following commands: + +- help (prints help) +- start [API PORT] [DISCOVERY PORT] [BOOTSTRAP NODE] - creates and starts the node, all parameters are mandatory. +- stop - stops and destroys the node +- upload [LOCAL PATH] - uploads a local file to the node. Shows simple progress, prints the CID on screen when done. +- download [CID] [LOCAL PATH] - downloads a remote CID to a local path. Shows simple progress, prints CID on screen when done. + +If needed, the code for libstorage can also be found under /home/giuliano/Work/Status/logos-storage-nim/library. diff --git a/easylibstorage.h b/easylibstorage.h new file mode 100644 index 0000000..bd6ebd7 --- /dev/null +++ b/easylibstorage.h @@ -0,0 +1,32 @@ +#ifndef STORAGECONSOLE_EASYLIBSTORAGE_H +#define STORAGECONSOLE_EASYLIBSTORAGE_H + +#include + +#define STORAGE_NODE void * +#define CID char * + +enum log_level { + WARN, + INFO, + DEBUG, +}; + +typedef struct { + int api_port; + int disc_port; + char *data_dir; + char *log_level; + char *bootstrap_node; +} node_config; + +typedef void (*progress_callback)(int total, int complete, int status); + +STORAGE_NODE e_storage_new(node_config config); +int e_storage_start(STORAGE_NODE node); +int e_storage_stop(STORAGE_NODE node); +int e_storage_destroy(STORAGE_NODE node); +CID e_storage_upload(STORAGE_NODE node, FILE *input, progress_callback cb); +STORAGE_NODE e_storage_download(STORAGE_NODE node, CID cid, FILE *output, progress_callback cb); + +#endif // STORAGECONSOLE_EASYLIBSTORAGE_H diff --git a/main.c b/main.c new file mode 100644 index 0000000..3ee4882 --- /dev/null +++ b/main.c @@ -0,0 +1,103 @@ +#include "easylibstorage.h" + +#include +#include +#include + +typedef struct { + void *ctx; +} console; + +typedef void (*fn)(void *, console *); + +struct command { + const char *name; + const char *desc; + fn command; +}; + +int n_commands(); +static const struct command commands[]; + +void cmd_help(void *_, console *c) { + printf("Commands:\n"); + for (int i = 0; i < n_commands(); i++) { + printf(" [%s]: %s\n", commands[i].name, commands[i].desc); + } +} + +void cmd_quit(void *_, console *c) { + +} + +void progress_print(int total, int complete, int status) { + if (total > 0) { + printf("\r %d / %d bytes", complete, total); + } else { + printf("\r %d bytes", complete); + } + fflush(stdout); +} + +void cmd_start(void *args, console *c) { + +} + +void cmd_stop(void *args, console *c) { + +} + +void cmd_upload(void *args, console *c) { + +} + +void cmd_download(void *args, console *c) { + +} + +static const struct command commands[] = { + {"help", "prints this help message", cmd_help}, + {"quit", "quits this program", cmd_quit}, + {"start", "[API PORT] [DISC PORT] [BOOTSTRAP NODE] creates and starts a node", cmd_start}, + {"stop", "stops and destroys the node", cmd_stop}, + {"upload", "[PATH] uploads a file to the node", cmd_upload}, + {"download", "[CID] [PATH] downloads content to a file", cmd_download}, +}; + +int n_commands() { return sizeof(commands) / sizeof(commands[0]); } + +int main(void) { + char buf[4096]; + console c; + int i; + + c.ctx = NULL; + + while (1) { + printf("> "); + fflush(stdout); + + if (!fgets(buf, sizeof(buf), stdin)) { + break; + } + buf[strcspn(buf, "\n")] = 0; + + char *cmd = strtok(buf, " "); + if (cmd == NULL) { + // user has input an empty string + continue; + } + + for (i = 0; i < n_commands(); i++) { + if (strcmp(cmd, commands[i].name) == 0) { + char *arg = strtok(NULL, " "); + commands[i].command(arg, &c); + break; + } + } + + if (i == n_commands()) { + printf("Invalid command %s\n", buf); + } + } +}