Skip to content

Commit

Permalink
Merge pull request #2 from auyer/c_sdk
Browse files Browse the repository at this point in the history
Creates a C SDK to interact with the server

Thanks Harith for the codereview 

https://codereview.stackexchange.com/questions/287460/small-http-client-sdk-created-using-libcurl-in-c-language/
  • Loading branch information
auyer authored Oct 17, 2023
2 parents 0ec8b80 + ef12f64 commit 635f086
Show file tree
Hide file tree
Showing 13 changed files with 676 additions and 3 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/clang-tidy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: clang-tidy-review

on:
pull_request:
paths:
- '**.c'
- '**.h'
workflow_dispatch:

jobs:
clang-tidy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install libcurl
run: sudo apt-get update && sudo apt-get install -y curl libcurl4-openssl-dev

- name: CMake
working-directory: ./sdks/c/
env:
CC: clang
run: cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

- name: reviewdog with clang-tidy
uses: arkedge/[email protected]
with:
workdir: ./sdks/c/build/

- name: Build
working-directory: ./sdks/c/
run: cmake -S . -B ./build
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
/target
sdks/c/build
.vscode/settings.json

17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ What I wanted to create:
- In the Rust programming language
- With a live feed of changes (it felt like a cool demo for my personal site)

I started implementing an http web server using Axum. I did this because I am used to working with http and rest interfaces. I might implement my own connection method, but this will be fine for now.
I started implementing an http web server using Axum. I did this because I am used to working with http and rest interfaces. I might implement my own connection protocol, but this will be fine for now.
Since I was using Axum I also added a `/metrics` endpoint for Prometheus.

The "database" part I created with a simple HashMap, and protected it behind a RWMutex.
Expand All @@ -38,5 +38,16 @@ A sample of the WAL received by a WebSocket client:
```

The live demo is available in my personal website: https://rcpassos.me/projects/kv
And the source to that is here:
https://github.com/auyer/auyer.github.io/blob/main/src/lib/services.ts
And the source for that is here:
https://github.com/auyer/auyer.github.io/blob/main/src/lib/services.js

# SDKs
I am also creating SDKs for this project using different languages.
Since the server uses REST with HTTP for the database interface, this is the main part that needs to be implemented in the SDKs.
The Live Feed is implemented using WebSockets, and is a less important feature.


| Feature/SDK | C | Rust
|---------------- |--- |------
| Rest Endpoints | x |
| Live Feed (WS) | |
57 changes: 57 additions & 0 deletions sdks/c/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
Language: Cpp
# BasedOnStyle: Mozilla
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakStringLiterals: true
ColumnLimit: 120
ContinuationIndentWidth: 4
DerivePointerAlignment: false
IncludeBlocks: Preserve
IndentCaseLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 4
IndentWrappedFunctionNames: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 100000
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Always
...
121 changes: 121 additions & 0 deletions sdks/c/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
Checks: '
-*,
clang-*,
bugprone-assert-side-effect,
bugprone-bool-pointer-implicit-conversion,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-widening-cast,
bugprone-multiple-statement-macro,
bugprone-sizeof-expression,
bugprone-suspicious-enum-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-terminating-continue,
bugprone-too-small-loop-variable,
cppcoreguidelines-avoid-goto,
misc-definitions-in-headers,
misc-misplaced-const,
misc-redundant-expression,
misc-unused-parameters,
readability-braces-around-statements,
readability-const-return-type,
readability-else-after-return,
readability-function-size,
readability-implicit-bool-conversion,
readability-inconsistent-declaration-parameter-name,
readability-isolate-declaration,
readability-magic-numbers,
readability-misplaced-array-index,
readability-named-parameter,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-preprocessor,
readability-uppercase-literal-suffix,
readability-identifier-naming,
'
WarningsAsErrors: '
bugprone-assert-side-effect,
bugprone-bool-pointer-implicit-conversion,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-widening-cast,
bugprone-multiple-statement-macro,
bugprone-sizeof-expression,
bugprone-suspicious-enum-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-terminating-continue,
bugprone-too-small-loop-variable,
cppcoreguidelines-avoid-goto,
misc-definitions-in-headers,
misc-misplaced-const,
misc-redundant-expression,
misc-unused-parameters,
readability-braces-around-statements,
readability-const-return-type,
readability-else-after-return,
readability-function-size,
readability-implicit-bool-conversion,
readability-inconsistent-declaration-parameter-name,
readability-isolate-declaration,
readability-magic-numbers,
readability-misplaced-array-index,
readability-named-parameter,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-preprocessor,
readability-uppercase-literal-suffix,
# readability-identifier-naming,
'

# From the docs: "Output warnings from headers matching this filter"
# But the goal should be to exclude(!) the headers for which clang-tidy is not called,
# e.g., for naming convention checks. DO NOT USE this field if you don't want to analyze
# header files just because they're included (seems to work).
# HeaderFilterRegex: '$'
# https://github.com/Kitware/CMake/blob/master/.clang-tidy
HeaderFilterRegex: '.*\.(h|hxx|cxx)$'
AnalyzeTemporaryDtors: false
FormatStyle: none
User: martin
CheckOptions:
- { key: bugprone-assert-side-effect.AssertMacros, value: assert }
- { key: bugprone-assert-side-effect.CheckFunctionCalls, value: '0' }
- { key: bugprone-misplaced-widening-cast.CheckImplicitCasts, value: '1' }
- { key: bugprone-sizeof-expression.WarnOnSizeOfConstant, value: '1' }
- { key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression, value: '1' }
- { key: bugprone-sizeof-expression.WarnOnSizeOfThis, value: '1' }
- { key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant, value: '1' }
- { key: bugprone-suspicious-enum-usage.StrictMode, value: '0' }
- { key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens, value: '5' }
- { key: bugprone-suspicious-missing-comma.RatioThreshold, value: '0.200000' }
- { key: bugprone-suspicious-missing-comma.SizeThreshold, value: '5' }
- { key: misc-definitions-in-headers.HeaderFileExtensions, value: ',h,hh,hpp,hxx' }
- { key: misc-definitions-in-headers.UseHeaderFileExtension, value: '1' }
- { key: readability-braces-around-statements.ShortStatementLines, value: '1' }
- { key: readability-function-size.LineThreshold, value: '500' }
- { key: readability-function-size.StatementThreshold, value: '800' }
- { key: readability-function-size.ParameterThreshold, value: '10' }
- { key: readability-function-size.NestingThreshold, value: '6' }
- { key: readability-function-size.VariableThreshold, value: '15' }
- { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: '0' }
- { key: readability-implicit-bool-conversion.AllowPointerConditions, value: '0' }
- { key: readability-implicit-bool-conversion.AllowPointerConditions, value: '0' }
- { key: readability-inconsistent-declaration-parameter-name.IgnoreMacros, value: '1' }
- { key: readability-inconsistent-declaration-parameter-name.Strict, value: '1' }
- { key: readability-magic-numbers.IgnoredFloatingPointValues, value: '1.0;100.0;' }
- { key: readability-magic-numbers.IgnoredIntegerValues, value: '1;2;3;4;' }
- { key: readability-magic-numbers.IgnorePowersOf2IntegerValues, value: '0' }
- { key: readability-magic-numbers.IgnoreAllFloatingPointValues, value: '0' }
- { key: readability-redundant-declaration.IgnoreMacros, value: '1' }
- { key: readability-redundant-function-ptr-dereference, value: '1' }
- { key: readability-uppercase-literal-suffix.IgnoreMacros, value: '0' }
- { key: readability-uppercase-literal-suffix.IgnoreMacros, value: '0' }
17 changes: 17 additions & 0 deletions sdks/c/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.27.6)

project(MemoryKV)

add_executable(${PROJECT_NAME} example.c)

set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 23)

find_package(PkgConfig REQUIRED)
pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0)

add_subdirectory(libMemoryKV)

target_include_directories(${PROJECT_NAME} PUBLIC libMemoryKV/)
target_link_directories(${PROJECT_NAME} PUBLIC libMemoryKV/)

target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::libcurl libMemoryKV)
5 changes: 5 additions & 0 deletions sdks/c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake:
cmake -S . -B build

cmake_make:
cd build && make
Empty file added sdks/c/build/.gitkeep
Empty file.
3 changes: 3 additions & 0 deletions sdks/c/compile_flags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-I./libMemoryKV
-llibMemoryKV
-std=c2x
95 changes: 95 additions & 0 deletions sdks/c/example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <curl/curl.h>
#include <libMemoryKV.h>
#include <stdio.h>
#include <stdlib.h>

void print_memkv_result(memkv_result *response) {
if (!response) {
fprintf(stderr, "Error: response is NULL\n");

} else if (response->success) {
printf("Success! Result: %s\n", response->result);
} else {
fprintf(stderr, "Error: %s\n", response->error);
}
free(response);
}

int main(void) {
memkv_client *client;

client = memkv_client_new("http://localhost:8080");

memkv_result *response;

char key[] = "c_sdk";
char key2[] = "c_sdk2";
char key3[] = "a_sdk";

fprintf(stdout, "This example will create, and delete in 3 keys: %s, %s and %s\n", key, key2, key3);

// put key
static const char put_body[] = "{"
" \"name\" : \"MemoryKV Example Body\","
" \"content\" : \"json\""
"}";

fprintf(stdout, "\nPut Key '%s'\n", key);
response = memkv_put_key(client, key, put_body);
print_memkv_result(response);

// list key
fprintf(stdout, "\nList Keys\n");
response = memkv_list_keys(client);
print_memkv_result(response);

// get key
fprintf(stdout, "\nGet Key '%s'\n", key);
response = memkv_get_key(client, key);
print_memkv_result(response);

fprintf(stdout, "\nPut Key '%s'\n", key2);
response = memkv_put_key(client, key2, put_body);
print_memkv_result(response);

fprintf(stdout, "\nPut Key '%s'\n", key3);
response = memkv_put_key(client, key3, put_body);
print_memkv_result(response);

fprintf(stdout, "\nPut Key '%s' (again)\n", key3);
response = memkv_put_key(client, key3, put_body);
print_memkv_result(response);

char prefix[] = "c";

fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix);
response = memkv_list_keys_with_prefix(client, prefix);
print_memkv_result(response);

prefix[0] = 'a';
fprintf(stdout, "\nList Keys With Prefix '%s'\n", prefix);
response = memkv_list_keys_with_prefix(client, prefix);
print_memkv_result(response);

// delete key
prefix[0] = 'c';
fprintf(stdout, "\nMaking a delete Key Request with Prefix '%s'\n", prefix);
response = memkv_delete_keys_with_prefix(client, prefix);
print_memkv_result(response);

// delete all keys
fprintf(stdout, "\nDelete all Keys\n");
response = memkv_delete_all_keys(client);
print_memkv_result(response);

fprintf(stdout, "\nMaking a delete Key '%s'\n", key3);
response = memkv_delete_key(client, key3);
print_memkv_result(response);

// list key
fprintf(stdout, "\nList Keys\n");
response = memkv_list_keys(client);
print_memkv_result(response);

free(client);
}
6 changes: 6 additions & 0 deletions sdks/c/libMemoryKV/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
add_library(libMemoryKV libMemoryKV.c)

find_package(PkgConfig REQUIRED)
pkg_check_modules(libcurl REQUIRED IMPORTED_TARGET libcurl>=7.17.0)

target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::libcurl)
Loading

0 comments on commit 635f086

Please sign in to comment.