Skip to content
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
1 change: 1 addition & 0 deletions foreign/cpp/.bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
201 changes: 167 additions & 34 deletions foreign/cpp/include/iggy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,129 @@

namespace iggy {

/// Exception raised by the C++ client when an operation fails.
class IggyException : public std::runtime_error {
public:
explicit IggyException(const char *message) : std::runtime_error(message) {}
explicit IggyException(const std::string &message) : std::runtime_error(message) {}
};

class CompressionAlgorithm final {
namespace detail {

/// Internal helper used by string-backed option types in this header.
template <typename Tag>
class StringTag {
protected:
explicit StringTag(std::string value) : value_(std::move(value)) {}
~StringTag() = default;

std::string_view value() const { return value_; }

private:
std::string value_;
};
Comment thread
hubcio marked this conversation as resolved.

} // namespace detail

/// Compression setting for `Client::create_topic(...)`.
///
/// Use this type to choose whether messages in a topic are stored as-is or compressed with gzip.
///
/// Internally, this value is passed across the Rust FFI as a string.
/// Values outside the supported set are rejected by the Rust client.
class CompressionAlgorithm final : private detail::StringTag<CompressionAlgorithm> {
public:
/// Store messages without compression.
static CompressionAlgorithm none() { return CompressionAlgorithm("none"); }
/// Compress messages with gzip.
static CompressionAlgorithm gzip() { return CompressionAlgorithm("gzip"); }

std::string_view compression_algorithm_value() const { return algorithm_; }
std::string_view compression_algorithm_value() const { return value(); }

private:
explicit CompressionAlgorithm(std::string algorithm) : algorithm_(std::move(algorithm)) {}

std::string algorithm_;
explicit CompressionAlgorithm(std::string algorithm)
: detail::StringTag<CompressionAlgorithm>(std::move(algorithm)) {}
};

class Expiry final {
/// Compression setting for `Client::snapshot(...)`.
///
/// Use this type to choose how snapshot data is compressed in the generated archive.
///
/// Internally, this value is passed across the Rust FFI as a string.
/// Values outside the supported set are rejected by the Rust client.
class SnapshotCompression final : private detail::StringTag<SnapshotCompression> {
public:
static Expiry server_default() { return Expiry("server_default", 0); }
static Expiry never_expire() { return Expiry("never_expire", std::numeric_limits<std::uint64_t>::max()); }
static Expiry duration(std::uint64_t micros) { return Expiry("duration", micros); }

std::string_view expiry_kind() const { return expiry_kind_; }
std::uint64_t expiry_value() const { return expiry_value_; }
/// Store snapshot files without compression.
static SnapshotCompression stored() { return SnapshotCompression("stored"); }
Comment thread
hubcio marked this conversation as resolved.
/// Use standard deflate compression.
static SnapshotCompression deflated() { return SnapshotCompression("deflated"); }
/// Use bzip2 for a higher compression ratio at the cost of slower processing.
static SnapshotCompression bzip2() { return SnapshotCompression("bzip2"); }
/// Use Zstandard for fast compression and decompression.
static SnapshotCompression zstd() { return SnapshotCompression("zstd"); }
/// Use LZMA for high compression, especially for larger files.
static SnapshotCompression lzma() { return SnapshotCompression("lzma"); }
/// Use XZ, which is similar to LZMA and often faster to decompress.
static SnapshotCompression xz() { return SnapshotCompression("xz"); }

std::string_view snapshot_compression_value() const { return value(); }
Comment thread
hubcio marked this conversation as resolved.

private:
explicit Expiry(std::string expiry_kind, std::uint64_t expiry_value)
: expiry_kind_(std::move(expiry_kind)), expiry_value_(expiry_value) {}
explicit SnapshotCompression(std::string snapshot_compression)
: detail::StringTag<SnapshotCompression>(std::move(snapshot_compression)) {}
};

std::string expiry_kind_;
std::uint64_t expiry_value_;
/// System snapshot selector for `Client::snapshot(...)`.
///
/// Use this type to choose which snapshot data the server should include.
///
/// Internally, each selected value is passed across the Rust FFI as a string.
/// Values outside the supported set are rejected by the Rust client.
class SystemSnapshotType final : private detail::StringTag<SystemSnapshotType> {
public:
/// Include an overview of the filesystem structure.
static SystemSnapshotType filesystem_overview() { return SystemSnapshotType("filesystem_overview"); }
/// Include the list of currently running processes.
static SystemSnapshotType process_list() { return SystemSnapshotType("process_list"); }
/// Include CPU, memory, and other system resource usage statistics.
static SystemSnapshotType resource_usage() { return SystemSnapshotType("resource_usage"); }
/// Include the test snapshot used for development and testing.
static SystemSnapshotType test() { return SystemSnapshotType("test"); }
/// Include server logs from the configured logging directory.
static SystemSnapshotType server_logs() { return SystemSnapshotType("server_logs"); }
/// Include the server configuration.
static SystemSnapshotType server_config() { return SystemSnapshotType("server_config"); }
/// Include all available snapshot types.
static SystemSnapshotType all() { return SystemSnapshotType("all"); }

std::string_view snapshot_type_value() const { return value(); }

private:
explicit SystemSnapshotType(std::string snapshot_type)
: detail::StringTag<SystemSnapshotType>(std::move(snapshot_type)) {}
};

class MaxTopicSize final {
/// Maximum retained size for a topic.
///
/// Use this type to choose whether a topic uses the server default limit, no limit, or an
/// explicit byte limit.
///
/// Internally, this value is passed across the Rust FFI as a string.
/// Values outside the supported set are rejected by the Rust client.
/// In addition to `server_default` and `unlimited`, the Rust parser also accepts
/// numeric size strings. A value of `0` is treated as `server_default`, and a
/// value of `std::numeric_limits<std::uint64_t>::max()` is treated as `unlimited`.
class MaxTopicSize final : private detail::StringTag<MaxTopicSize> {
public:
/// Use the server's default maximum topic size.
static MaxTopicSize server_default() { return MaxTopicSize("server_default"); }
/// Disable the maximum topic size limit.
static MaxTopicSize unlimited() { return MaxTopicSize("unlimited"); }
/// Set an explicit maximum topic size in bytes.
///
/// A value of `0` is treated as `server_default()`. A value of
/// `std::numeric_limits<std::uint64_t>::max()` is treated as `unlimited()`.
/// The configured limit cannot be lower than the server's segment size.
static MaxTopicSize from_bytes(std::uint64_t bytes) {
if (bytes == 0) {
return server_default();
Expand All @@ -76,20 +159,84 @@ class MaxTopicSize final {
return MaxTopicSize(std::to_string(bytes));
}

std::string_view max_topic_size() const { return max_topic_size_; }
std::string_view max_topic_size() const { return value(); }

private:
explicit MaxTopicSize(std::string max_topic_size) : detail::StringTag<MaxTopicSize>(std::move(max_topic_size)) {}
};

// TODO(slbotbm): Add rust bindings for Identifier that will use IdKind
/// Describes whether an identifier is numeric or string-based.
///
/// Use this type to describe how an identifier is encoded.
///
/// This type is reserved for future identifier bindings and is not currently passed through the
/// Rust FFI.
class IdKind final : private detail::StringTag<IdKind> {
public:
/// A numeric identifier represented as a 32-bit integer.
static IdKind numeric() { return IdKind("numeric"); }
/// A string identifier represented by its text value.
static IdKind string() { return IdKind("string"); }

std::string_view id_kind_value() const { return value(); }

private:
explicit MaxTopicSize(std::string max_topic_size) : max_topic_size_(std::move(max_topic_size)) {}
explicit IdKind(std::string id_kind) : detail::StringTag<IdKind>(std::move(id_kind)) {}
};

std::string max_topic_size_;
/// Message expiry policy for `Client::create_topic(...)`.
///
/// Use this type to choose how long messages in a topic are retained.
///
/// `expiry_kind()` returns the selected mode. `expiry_value()` returns the associated payload:
/// the duration for `duration(micros)`, `0` for `server_default()`, or
/// `std::numeric_limits<std::uint64_t>::max()` for `never_expire()`.
///
/// Internally, this value is passed across the Rust FFI as a kind/value pair.
/// Unsupported kinds are rejected by the Rust client.
class Expiry final {
public:
/// Use the server's default message expiry policy.
static Expiry server_default() { return Expiry("server_default", 0); }
/// Keep messages until they are removed for some other reason, such as topic deletion.
static Expiry never_expire() { return Expiry("never_expire", std::numeric_limits<std::uint64_t>::max()); }
/// Expire messages after the given number of microseconds.
static Expiry duration(std::uint64_t micros) { return Expiry("duration", micros); }

std::string_view expiry_kind() const { return expiry_kind_; }
std::uint64_t expiry_value() const { return expiry_value_; }

private:
explicit Expiry(std::string expiry_kind, std::uint64_t expiry_value)
: expiry_kind_(std::move(expiry_kind)), expiry_value_(expiry_value) {}

std::string expiry_kind_;
std::uint64_t expiry_value_;
};

/// Starting position for `Client::poll_messages(...)`.
///
/// Use this type to choose where the server should begin reading messages.
///
/// `polling_strategy_kind()` returns the selected mode. `polling_strategy_value()` returns the
/// associated offset or timestamp for the parameterized modes and `0` for the others.
///
/// Internally, this value is passed across the Rust FFI as a kind/value pair.
/// Unsupported kinds are rejected by the Rust client.
class PollingStrategy final {
public:
/// Start polling from a specific message offset.
static PollingStrategy offset(std::uint64_t value) { return PollingStrategy("offset", value); }
/// Start polling from a specific timestamp.
static PollingStrategy timestamp(std::uint64_t value) { return PollingStrategy("timestamp", value); }
/// Start polling from the first message in the partition.
static PollingStrategy first() { return PollingStrategy("first", 0); }
/// Start polling from the last message currently available in the partition.
static PollingStrategy last() { return PollingStrategy("last", 0); }
/// Start polling from the next message after the stored consumer offset.
///
/// This is typically used with automatic offset commits enabled.
static PollingStrategy next() { return PollingStrategy("next", 0); }

std::string_view polling_strategy_kind() const { return polling_strategy_kind_; }
Expand All @@ -103,18 +250,4 @@ class PollingStrategy final {
std::uint64_t polling_strategy_value_;
};

// TODO(slbotbm): Add rust bindings for Identifier that will use IdKind
class IdKind final {
public:
static IdKind numeric() { return IdKind("numeric"); }
static IdKind string() { return IdKind("string"); }

std::string_view id_kind_value() const { return id_kind_; }

private:
explicit IdKind(std::string id_kind) : id_kind_(std::move(id_kind)) {}

std::string id_kind_;
};

} // namespace iggy
Loading
Loading