/// # Tutorial 0: Introduction and Common Patterns /// /// Welcome to the first `logos-libp2p-module` tutorial! /// /// Before creating nodes or connecting peers, it helps to understand the /// conventions used by every tutorial in this series. /// /// The C++ wrapper (`logos-libp2p-module`) exposes a single main class, `Libp2pModuleImpl`. /// Most methods on that class return the Logos result type. In C++ module code that /// type is named `StdLogosResult`. /// /// `StdLogosResult` has three fields: /// /// | Field | Meaning | /// |-------|---------| /// | `success` | `true` when the call completed successfully | /// | `value` | a `nlohmann::json` value returned by the call | /// | `error` | a diagnostic string when `success == false` | /// /// ## Always Check `success` First /// /// Every tutorial follows the same rule: check unsuccessful outcomes before /// using returned data. /// /// ```cpp /// StdLogosResult info = node.peerInfo(); /// if (!info.success) { /// fprintf(stderr, "Failed to get peer info: %s\n", /// info.error.c_str()); /// return 1; /// } /// ``` /// /// This keeps the code predictable: errors are handled immediately, and the /// rest of the step can assume the operation succeeded. /// /// ## Tutorial Executables Fail Fast /// /// These programs are examples, not long-running services. When a required /// operation fails, they print a useful error message to `stderr` and return /// `1`. Successful tutorials print progress to `stdout` and return `0`. /// /// ## Controlling libp2p Logs /// /// The wrapped libp2p binding can emit runtime logs. Tutorials set the log /// level to `"fatal"` by default so `stdout` only shows the tutorial's /// own progress messages during normal runs. Choose another level such as /// `"info"`, `"debug"`, or `"trace"` when you need more /// detail while debugging: /// /// ```cpp /// setLogLevel("debug"); /// // ... or /// Libp2pModuleImpl::setLogLevel("debug"); /// ``` /// /// The level names are `"none"`, `"trace"`, `"debug"`, `"info"`, `"notice"`, /// `"warn"`, `"error"` and `"fatal"`; any other name returns a failed result /// and leaves the level as it was. /// /// Log levels are inclusive minimum thresholds: `"trace"` emits trace /// and above, `"debug"` emits debug and above, and so on. /// `"none"` is the lowest threshold, so it emits all logs; it does not /// disable logging. Use `"fatal"` for the quietest built-in threshold. /// /// In your own application, `"error"` is often a useful default. /// It keeps normal output quiet while still surfacing conditions /// that may indicate libp2p is misbehaving or that your integration code needs an /// adjustment. Some error logs describe remote-peer behavior, retries, or /// recoverable internal state, so they may not require any action from your side. /// /// ## Convert JSON Values Explicitly /// /// The `value` field is JSON. Convert it to the C++ type you need at the /// point where you use it: /// /// ```cpp /// std::string peerId = info.value["peerId"].get(); /// /// for (const nlohmann::json& addr : info.value["addrs"]) { /// printf(" %s\n", addr.get().c_str()); /// } /// ``` /// /// Scalar values use `get()`. Objects are accessed by key. Arrays are /// iterated with range-for loops. /// /// Tutorial code assumes returned JSON values have the documented type, so it /// uses and converts values directly instead of checking the JSON type first. /// /// ## Binary Data May Be Encoded /// /// Some APIs carry arbitrary bytes, such as stream reads, DHT values, public /// keys, or service discovery records. Those values may be returned as strings /// encoded for JSON transport. The tutorials decode them before comparing or /// printing human-readable payloads. /// /// ----------- #include #include #include "plugin.h" int main() { printf("=== Tutorial 0: Introduction and Common Patterns ===\n\n"); // Silence logs from wrapped library. setLogLevel("fatal"); /// ## Step 1: Create and start a node /// /// Even the first real operation returns a result. Check it before moving on. Libp2pModuleImpl node; StdLogosResult startRes = node.start(); if (!startRes.success) { fprintf(stderr, "Failed to start node: %s\n", startRes.error.c_str()); return 1; } printf("Node started\n"); /// ## Step 2: Use scalar JSON values /// /// Some calls return a single string, number, or boolean in `value`. StdLogosResult version = node.getNodeInfo("Version"); if (!version.success) { fprintf(stderr, "Failed to get module version: %s\n", version.error.c_str()); return 1; } printf("Module version: %s\n", version.value.get().c_str()); /// ## Step 3: Use JSON objects and arrays /// /// `peerInfo()` returns a JSON object. The `peerId` field is a string, while /// `addrs` is an array because a node may listen on multiple addresses. StdLogosResult info = node.peerInfo(); if (!info.success) { fprintf(stderr, "Failed to get peer info: %s\n", info.error.c_str()); return 1; } std::string peerId = info.value["peerId"].get(); printf("Peer ID: %s\n", peerId.c_str()); printf("Listening addresses:\n"); for (const nlohmann::json& addr : info.value["addrs"]) { printf(" %s\n", addr.get().c_str()); } /// ## Step 4: Stop cleanly /// /// Production code should always attempt to close resources gracefully first. /// /// The tutorials do not always process every cleanup outcome, such as failures /// while stopping a node or closing streams, in order to keep the example code /// focused and easy to follow. StdLogosResult stopRes = node.stop(); if (!stopRes.success) { fprintf(stderr, "Failed to stop node: %s\n", stopRes.error.c_str()); return 1; } printf("\n=== Tutorial 0 Complete ===\n"); return 0; } /// ## Summary /// /// In this tutorial you learned the conventions used everywhere else: /// - Most calls return `StdLogosResult` /// - Check `success` before reading `value` /// - Print `error` when a call fails /// - Convert JSON values with `get()`, object keys, or array iteration /// - Return `1` from tutorial executables when required operations fail /// ## Run tutorial /// /// Run this tutorial now to check that your environment is set up and the /// tutorial executable has already been built: /// /// ```bash /// ./build/tutorial/tutorial_0_introduction /// ``` /// /// If that command fails because the tutorial has not been built yet, build the /// tutorials with this command and then run it again: /// /// ```bash /// nix --extra-experimental-features 'nix-command flakes' develop --command ./tutorial/build_tutorials.sh /// ``` /// ## Exercise: Enable libp2p debug logs /// /// Change this tutorial's log level from `"fatal"` to `"debug"`: /// /// ```cpp /// setLogLevel("debug"); /// ``` /// /// Compile the tutorial binaries again, using the same command from above. /// /// Run tutorial 0 again. You should now see libp2p debug logs in `stdout` /// alongside the tutorial's own progress messages.