Skip to content

[CDRIVER-5662] Tutorials for simple CRUD, and some more convience APIs #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ IncludeCategories:
InsertNewlineAtEOF: true
IfMacros:
- mlib_math_catch
- amongoc_if_error
ForEachMacros:
- bson_foreach
AllowBreakBeforeNoexceptSpecifier: Always
---
# For some reason, Clang sees some files as Objective-C. Add this section just to appease it.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ build:
test: build
cmake -E chdir "$(BUILD_DIR)" ctest -C "$(CONFIG)" --output-on-failure -j8

all_sources := $(shell find $(THIS_DIR)/src/ $(THIS_DIR)/include/ $(THIS_DIR)/tests/ -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp')
all_sources := $(shell find $(THIS_DIR)/src/ $(THIS_DIR)/include/ $(THIS_DIR)/tests/ $(THIS_DIR)/docs/ -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp')
format-check: poetry-install
$(POETRY) run python tools/include-fixup.py --check
$(POETRY) run $(MAKE) _format-check
Expand Down
4 changes: 4 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ def generate_sphinx_inventory(
.. |static| replace:: :cpp:`static`
.. |const| replace:: :cpp:`const`
.. |void| replace:: :cpp:`void`
.. |char| replace:: :cpp:`char`
.. |A| replace:: :math:`A`
.. |A'| replace:: :math:`A'`
.. |B| replace:: :math:`B`
Expand Down Expand Up @@ -337,6 +338,7 @@ def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> No
"default_loop",
"client",
"collection",
"cursor",
)
),
*(
Expand Down Expand Up @@ -414,6 +416,8 @@ class CustomCppLexer(CppLexer):
(r"bson_type_\w+\b", token.Name.Constant),
# Other macro
(r"bson_foreach\w*\b", token.Keyword),
(r"amongoc_declmsg\b", token.Keyword),
(r"amongoc_if_error\b", token.Keyword),
inherit,
],
}
Expand Down
87 changes: 13 additions & 74 deletions docs/how-to/communicate.example.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include <amongoc/amongoc.h>

#include "bson/view.h"
#include <bson/format.h>
#include <bson/iterator.h>
#include <bson/mut.h>

#include "mlib/str.h"
#include <bson/view.h>

/**
* @brief Shared state for the application. This is passed through the app as pointer stored
Expand All @@ -15,11 +14,6 @@ typedef struct app_state {
amongoc_client* client;
} app_state;

/**
* @brief Write the content of a BSON document in JSON-like format to the given output
*/
static void print_bson(FILE* into, bson_view doc, mlib_str_view indent);

/** after_hello()
* @brief Handle the `hello` response from the server
*
Expand All @@ -32,7 +26,7 @@ amongoc_box after_hello(amongoc_box state_ptr, amongoc_status*, amongoc_box resp
bson_view resp = bson_view_from(amongoc_box_cast(bson_doc, resp_data));
// Just print the response message
fprintf(stdout, "Got response: ");
print_bson(stdout, resp, mlib_str_view_from(""));
bson_write_repr(stdout, resp);
fputs("\n", stdout);
amongoc_box_destroy(resp_data);
return amongoc_nil;
Expand Down Expand Up @@ -72,8 +66,12 @@ int main(int argc, char const* const* argv) {
}
const char* const uri = argv[1];

amongoc_loop loop;
amongoc_default_loop_init(&loop);
amongoc_loop loop;
amongoc_status status = amongoc_default_loop_init(&loop);
amongoc_if_error (status, msg) {
fprintf(stderr, "Error setting up the event loop: %s\n", msg);
return 2;
}

struct app_state state = {0};

Expand All @@ -88,8 +86,7 @@ int main(int argc, char const* const* argv) {
amongoc_box_pointer(&state),
after_connect_say_hello);

amongoc_status fin_status = amongoc_okay;
amongoc_operation op = amongoc_tie(em, &fin_status, NULL, mlib_default_allocator);
amongoc_operation op = amongoc_tie(em, &status);
amongoc_start(&op);
amongoc_default_loop_run(&loop);
amongoc_operation_delete(op);
Expand All @@ -98,71 +95,13 @@ int main(int argc, char const* const* argv) {
amongoc_client_delete(state.client);
amongoc_default_loop_destroy(&loop);

if (amongoc_is_error(fin_status)) {
char* m = amongoc_status_strdup_message(fin_status);
fprintf(stderr, "An error occurred: %s\n", m);
free(m);
// Final status
amongoc_if_error (status, msg) {
fprintf(stderr, "An error occurred: %s\n", msg);
return 2;
} else {
printf("Okay\n");
return 0;
}
}
// end.

static void print_bson(FILE* into, bson_view doc, mlib_str_view indent) {
fprintf(into, "{\n");
bson_foreach(it, doc) {
mlib_str_view str = bson_key(it);
fprintf(into, "%*s \"%s\": ", (int)indent.len, indent.data, str.data);
bson_value_ref val = bson_iterator_value(it);
switch (val.type) {
case bson_type_eod:
case bson_type_double:
fprintf(into, "%f,\n", val.double_);
break;
case bson_type_utf8:
fprintf(into, "\"%s\",\n", val.utf8.data);
break;
case bson_type_document:
case bson_type_array: {
mlib_str i2 = mlib_str_append(indent, " ");
bson_view subdoc = bson_iterator_value(it).document;
print_bson(into, subdoc, mlib_str_view_from(i2));
mlib_str_delete(i2);
fprintf(into, ",\n");
break;
}
case bson_type_undefined:
fprintf(into, "[undefined],\n");
break;
case bson_type_bool:
fprintf(into, val.bool_ ? "true,\n" : "false,\n");
break;
case bson_type_null:
fprintf(into, "null,\n");
break;
case bson_type_int32:
fprintf(into, "%d,\n", val.int32);
break;
case bson_type_int64:
fprintf(into, "%ld,\n", val.int64);
break;
case bson_type_timestamp:
case bson_type_decimal128:
case bson_type_maxkey:
case bson_type_minkey:
case bson_type_oid:
case bson_type_binary:
case bson_type_datetime:
case bson_type_regex:
case bson_type_dbpointer:
case bson_type_code:
case bson_type_symbol:
case bson_type_codewscope:
fprintf(into, "[[printing unimplemented for this type]],\n");
break;
}
}
fprintf(into, "%*s}", (int)indent.len, indent.data);
}
11 changes: 4 additions & 7 deletions docs/how-to/communicate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The first "interesting" code will declare and initialize the default event loop:
.. literalinclude:: communicate.example.c
:lineno-match:
:start-at: loop;
:end-at: );
:end-at: }

.. seealso:: `amongoc_loop` and `amongoc_default_loop_init`

Expand Down Expand Up @@ -204,14 +204,11 @@ the first continuation, we use `amongoc_tie` to convert the emitter to an

.. literalinclude:: communicate.example.c
:lineno-match:
:start-at: fin_status
:start-at: amongoc_tie
:end-at: amongoc_tie

This will allow us to see the final result status of the program in
``fin_status`` after the returned operation ``op`` completes. We pass ``NULL``
for the `amongoc_tie::value` parameter, indicating that we do not care what the
final result value will be (in a successful case, this would just be the
`amongoc_nil` returned from ``after_hello``).
``fin_status`` after the returned operation ``op`` completes.


Start the Operation, Run the Loop, and Clean Up
Expand Down Expand Up @@ -243,7 +240,7 @@ Print the Final Result

.. literalinclude:: communicate.example.c
:lineno-match:
:start-at: is_error
:start-at: // Final status
:end-before: end.

Finally, we inspect the `amongoc_status` that was produced by our operation and
Expand Down
1 change: 1 addition & 0 deletions docs/how-to/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ How-to Guides
:caption: Guides
:maxdepth: 2

status-handling
communicate
looping
20 changes: 11 additions & 9 deletions docs/how-to/looping.example.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ typedef struct {
*
* @param state_ptr Pointer to the `state` for the program
*/
amongoc_emitter loop_step(amongoc_box state_ptr, amongoc_status prev_status, amongoc_box prev_res) {
amongoc_emitter loop_step(amongoc_box state_ptr, //
amongoc_status prev_status,
amongoc_box prev_res) {
(void)prev_res;
(void)prev_status;
// Print our status
Expand Down Expand Up @@ -60,7 +62,10 @@ int main(int argc, char const* const* argv) {

// Create a default loop
amongoc_loop loop;
amongoc_default_loop_init(&loop);
amongoc_if_error (amongoc_default_loop_init(&loop), msg) {
fprintf(stderr, "Error initializing event loop: %s\n", msg);
return 1;
}

// Seed the initial sum
state app_state = {.countdown = delay, .loop = &loop, .a = 0, .b = 1};
Expand All @@ -70,22 +75,19 @@ int main(int argc, char const* const* argv) {
// Tie the final result for later, and start the program
amongoc_status status;
amongoc_box result;
amongoc_operation op = amongoc_tie(em, &status, &result, mlib_default_allocator);
amongoc_operation op = amongoc_tie(em, &status, &result);
amongoc_start(&op);
// Run the program within the event loop
amongoc_default_loop_run(&loop);
amongoc_operation_delete(op);
amongoc_default_loop_destroy(&loop);

if (amongoc_is_error(status)) {
char* msg = amongoc_status_strdup_message(status);
amongoc_if_error (status, msg) {
fprintf(stderr, "error: %s\n", msg);
free(msg);
amongoc_box_destroy(result);
return 2;
} else {
// Get the value returned with `amongoc_just` in `loop_step`
printf("Got final value: %lu\n", amongoc_box_cast(uint64_t, result));
}
// Get the value returned with `amongoc_just` in `loop_step`
printf("Got final value: %lu\n", amongoc_box_cast(uint64_t, result));
return 0;
}
10 changes: 5 additions & 5 deletions docs/how-to/looping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,14 @@ program. We check for errors, either printing the error message or printing the
final result:

.. literalinclude:: looping.example.c
:start-at: is_error
:start-at: if_error (status, msg)
:end-at: return 0;
:lineno-match:

We use `amongoc_is_error` to test the final status for an error condition. If it
is an error, we get and print the error message to stderr, and we must destroy
the final result box because it may contain an unspecified value related to the
error, but we don't want to do anything with it.
We use :c:macro:`amongoc_if_error` to test the final status for an error
condition. If it is an error, we get and print the error message to stderr, and
we must destroy the final result box because it may contain an unspecified value
related to the error, but we don't want to do anything with it.

In the success case, we extract the value returned in `amongoc_just` as a
``uint64_t`` and print it to stdout. Note that because the box returned by
Expand Down
Loading