[libnbd PATCH 00/10] Rust bindings
by Tage Johansson
Hello,
This is a, compared to last time, much more complete implementation of
Rust bindings for Libnbd. There is still no documentation and no
examples. And one does still need LLVM to build the bindings due to
[rust-bindgen](https://github.com/rust-lang/rust-bindgen). But the
overall implementation is more or less complete, including integration
tests.
As you can see, there are 10 patches:
- The first is just a small tweek of the generator utils functions.
- The second implements the basic form of the Rust bindings.
- The third and fourth modifies generator/API.ml* a bit to enable some
of the more convenient features of the Rust bindings.
- The 5th and 6th patch implements some of those conveniences.
- The 7th and 8th implements tests for the Rust bindings as
created so far.
- The 9th patch is quite a large extention of the Rust bindings. It
implements another handle-type which makes use of rust's asynchronous
functions and the [Tokio runtime](https://tokio.rs) to make an
ergonomic and rusty asynchronous API for Libnbd.
- And the last patch implements integration tests for those asynchronous
bindings.
Please comment on anything from code to commit messages. The Rust
bindings are of course not complete with these patches. The goal is
still to get rid of the dependency on rust-bindgen and LLVM. And there
needs to be documentation and thoroughly tested examples.
Best regards,
Tage
Tage Johansson (10):
generator: Add copyright argument to `generate_header`
rust: create basic Rust bindings
generator: Add information about asynchronous handle calls
generator: Add information about the lifetime of closures
rust: Use FnOnce for callbacks which only will be used once
rust: Don't enforce 'static lifetimes on some closures
rust: Add a couple of integration tests
rust: Make it possible to run tests with Valgrind
rust: async: Create an async friendly handle type
rust: async: Add a couple of integration tests
.gitignore | 8 +
.ocamlformat | 4 +
Makefile.am | 1 +
configure.ac | 13 +
generator/API.ml | 43 +
generator/API.mli | 24 +
generator/Makefile.am | 2 +
generator/Rust.ml | 835 ++++++++++++++++++
generator/Rust.mli | 22 +
generator/generator.ml | 3 +
generator/utils.ml | 5 +-
generator/utils.mli | 3 +-
rust/Cargo.toml | 55 ++
rust/Makefile.am | 65 ++
rust/libnbd-sys/Cargo.toml | 30 +
rust/libnbd-sys/build.rs | 60 ++
rust/libnbd-sys/src/lib.rs | 24 +
rust/libnbd-sys/wrapper.h | 18 +
rust/run-tests.sh | 30 +
rust/src/async_handle.rs | 222 +++++
rust/src/error.rs | 118 +++
rust/src/handle.rs | 67 ++
rust/src/lib.rs | 32 +
rust/src/types.rs | 20 +
rust/src/utils.rs | 23 +
rust/tests/nbdkit_pattern/mod.rs | 28 +
rust/tests/test_100_handle.rs | 25 +
rust/tests/test_110_defaults.rs | 33 +
rust/tests/test_120_set_non_defaults.rs | 56 ++
rust/tests/test_130_private_data.rs | 28 +
rust/tests/test_140_explicit_close.rs | 31 +
rust/tests/test_200_connect_command.rs | 33 +
rust/tests/test_210_opt_abort.rs | 39 +
rust/tests/test_220_opt_list.rs | 85 ++
rust/tests/test_230_opt_info.rs | 124 +++
rust/tests/test_240_opt_list_meta.rs | 151 ++++
rust/tests/test_245_opt_list_meta_queries.rs | 98 ++
rust/tests/test_250_opt_set_meta.rs | 124 +++
rust/tests/test_255_opt_set_meta_queries.rs | 111 +++
rust/tests/test_300_get_size.rs | 36 +
rust/tests/test_400_pread.rs | 40 +
rust/tests/test_405_pread_structured.rs | 80 ++
rust/tests/test_410_pwrite.rs | 62 ++
rust/tests/test_460_block_status.rs | 96 ++
rust/tests/test_620_stats.rs | 76 ++
rust/tests/test_async_100_handle.rs | 25 +
rust/tests/test_async_200_connect_command.rs | 34 +
rust/tests/test_async_210_opt_abort.rs | 40 +
rust/tests/test_async_220_opt_list.rs | 86 ++
rust/tests/test_async_230_opt_info.rs | 126 +++
rust/tests/test_async_235memleak.rs | 57 ++
rust/tests/test_async_240_opt_list_meta.rs | 151 ++++
.../test_async_245_opt_list_meta_queries.rs | 96 ++
rust/tests/test_async_250_opt_set_meta.rs | 123 +++
.../test_async_255_opt_set_meta_queries.rs | 111 +++
rust/tests/test_async_400_pread.rs | 41 +
rust/tests/test_async_405_pread_structured.rs | 85 ++
rust/tests/test_async_410_pwrite.rs | 63 ++
rust/tests/test_async_460_block_status.rs | 96 ++
rust/tests/test_async_620_stats.rs | 77 ++
rust/tests/test_log/mod.rs | 86 ++
rustfmt.toml | 19 +
62 files changed, 4396 insertions(+), 3 deletions(-)
create mode 100644 .ocamlformat
create mode 100644 generator/Rust.ml
create mode 100644 generator/Rust.mli
create mode 100644 rust/Cargo.toml
create mode 100644 rust/Makefile.am
create mode 100644 rust/libnbd-sys/Cargo.toml
create mode 100644 rust/libnbd-sys/build.rs
create mode 100644 rust/libnbd-sys/src/lib.rs
create mode 100644 rust/libnbd-sys/wrapper.h
create mode 100755 rust/run-tests.sh
create mode 100644 rust/src/async_handle.rs
create mode 100644 rust/src/error.rs
create mode 100644 rust/src/handle.rs
create mode 100644 rust/src/lib.rs
create mode 100644 rust/src/types.rs
create mode 100644 rust/src/utils.rs
create mode 100644 rust/tests/nbdkit_pattern/mod.rs
create mode 100644 rust/tests/test_100_handle.rs
create mode 100644 rust/tests/test_110_defaults.rs
create mode 100644 rust/tests/test_120_set_non_defaults.rs
create mode 100644 rust/tests/test_130_private_data.rs
create mode 100644 rust/tests/test_140_explicit_close.rs
create mode 100644 rust/tests/test_200_connect_command.rs
create mode 100644 rust/tests/test_210_opt_abort.rs
create mode 100644 rust/tests/test_220_opt_list.rs
create mode 100644 rust/tests/test_230_opt_info.rs
create mode 100644 rust/tests/test_240_opt_list_meta.rs
create mode 100644 rust/tests/test_245_opt_list_meta_queries.rs
create mode 100644 rust/tests/test_250_opt_set_meta.rs
create mode 100644 rust/tests/test_255_opt_set_meta_queries.rs
create mode 100644 rust/tests/test_300_get_size.rs
create mode 100644 rust/tests/test_400_pread.rs
create mode 100644 rust/tests/test_405_pread_structured.rs
create mode 100644 rust/tests/test_410_pwrite.rs
create mode 100644 rust/tests/test_460_block_status.rs
create mode 100644 rust/tests/test_620_stats.rs
create mode 100644 rust/tests/test_async_100_handle.rs
create mode 100644 rust/tests/test_async_200_connect_command.rs
create mode 100644 rust/tests/test_async_210_opt_abort.rs
create mode 100644 rust/tests/test_async_220_opt_list.rs
create mode 100644 rust/tests/test_async_230_opt_info.rs
create mode 100644 rust/tests/test_async_235memleak.rs
create mode 100644 rust/tests/test_async_240_opt_list_meta.rs
create mode 100644 rust/tests/test_async_245_opt_list_meta_queries.rs
create mode 100644 rust/tests/test_async_250_opt_set_meta.rs
create mode 100644 rust/tests/test_async_255_opt_set_meta_queries.rs
create mode 100644 rust/tests/test_async_400_pread.rs
create mode 100644 rust/tests/test_async_405_pread_structured.rs
create mode 100644 rust/tests/test_async_410_pwrite.rs
create mode 100644 rust/tests/test_async_460_block_status.rs
create mode 100644 rust/tests/test_async_620_stats.rs
create mode 100644 rust/tests/test_log/mod.rs
create mode 100644 rustfmt.toml
--
2.41.0
1 year, 2 months
[libnbd PATCH v8 00/10] Rust Bindings for Libnbd
by Tage Johansson
This is the 8th version of the Rust bindings for libnbd. Compared to v7,
it (amongst others) removes the cblifetime and cbcount fields in API.ml.
It also contains patches for improving the examples, reformatting rust
files, and correcting the license field in Cargo.toml.
Best regards,
Tage
Tage Johansson (10):
rust: Specify minimum Rust version in Cargo.toml
rust: Correct license field in Cargo.toml
rust: Format some files to conform to rustfmt and rustfmt.toml
rust: Make it possible to run examples with a URI
generator: Add information about asynchronous handle calls
rust: async: Create an async friendly handle type
generator: Add `modifies_fd` flag to the [call] structure
rust: async: Use the modifies_fd flag to exclude calls
rust: async: Add a couple of integration tests
rust: async: Add an example
generator/API.ml | 64 +++++
generator/API.mli | 18 ++
generator/Rust.ml | 243 +++++++++++++++-
generator/Rust.mli | 2 +
generator/generator.ml | 2 +
rust/Cargo.toml | 12 +-
rust/Makefile.am | 2 +
rust/cargo_test/Cargo.toml | 1 +
rust/examples/concurrent-read-write.rs | 149 ++++++++++
rust/examples/connect-command.rs | 1 -
rust/examples/fetch-first-sector.rs | 15 +-
rust/examples/get-size.rs | 20 +-
rust/libnbd-sys/Cargo.toml | 1 +
rust/run-tests.sh.in | 2 +
rust/src/async_handle.rs | 268 ++++++++++++++++++
rust/src/lib.rs | 8 +
rust/src/utils.rs | 9 +
rust/tests/test_200_connect_command.rs | 11 +-
rust/tests/test_220_opt_list.rs | 2 +-
rust/tests/test_240_opt_list_meta.rs | 2 +-
rust/tests/test_245_opt_list_meta_queries.rs | 2 +-
rust/tests/test_250_opt_set_meta.rs | 2 +-
rust/tests/test_255_opt_set_meta_queries.rs | 2 +-
rust/tests/test_300_get_size.rs | 1 -
rust/tests/test_460_block_status.rs | 2 +-
rust/tests/test_620_stats.rs | 11 +-
rust/tests/test_async_100_handle.rs | 25 ++
rust/tests/test_async_200_connect_command.rs | 26 ++
rust/tests/test_async_210_opt_abort.rs | 32 +++
rust/tests/test_async_220_opt_list.rs | 86 ++++++
rust/tests/test_async_230_opt_info.rs | 122 ++++++++
rust/tests/test_async_240_opt_list_meta.rs | 150 ++++++++++
.../test_async_245_opt_list_meta_queries.rs | 94 ++++++
rust/tests/test_async_250_opt_set_meta.rs | 125 ++++++++
.../test_async_255_opt_set_meta_queries.rs | 110 +++++++
rust/tests/test_async_400_pread.rs | 40 +++
rust/tests/test_async_405_pread_structured.rs | 84 ++++++
rust/tests/test_async_410_pwrite.rs | 59 ++++
rust/tests/test_async_460_block_status.rs | 98 +++++++
rust/tests/test_async_620_stats.rs | 69 +++++
scripts/git.orderfile | 1 +
41 files changed, 1930 insertions(+), 43 deletions(-)
create mode 100644 rust/examples/concurrent-read-write.rs
create mode 100644 rust/src/async_handle.rs
create mode 100644 rust/tests/test_async_100_handle.rs
create mode 100644 rust/tests/test_async_200_connect_command.rs
create mode 100644 rust/tests/test_async_210_opt_abort.rs
create mode 100644 rust/tests/test_async_220_opt_list.rs
create mode 100644 rust/tests/test_async_230_opt_info.rs
create mode 100644 rust/tests/test_async_240_opt_list_meta.rs
create mode 100644 rust/tests/test_async_245_opt_list_meta_queries.rs
create mode 100644 rust/tests/test_async_250_opt_set_meta.rs
create mode 100644 rust/tests/test_async_255_opt_set_meta_queries.rs
create mode 100644 rust/tests/test_async_400_pread.rs
create mode 100644 rust/tests/test_async_405_pread_structured.rs
create mode 100644 rust/tests/test_async_410_pwrite.rs
create mode 100644 rust/tests/test_async_460_block_status.rs
create mode 100644 rust/tests/test_async_620_stats.rs
base-commit: f2dac1102884e3dea1cfb33479b34dd689fbb670
prerequisite-patch-id: ce5d2f65bb12ecda61c97fdf22255e188016b3fc
prerequisite-patch-id: cb5e3f05b600a4953e2a77bd53067bb51903aecd
prerequisite-patch-id: 7613cb6ebcc41fb45da587fc9487eb6c643a14a4
prerequisite-patch-id: 397ff0bea47242cf549a894ce519b3702f072c44
prerequisite-patch-id: d6bcb838a1875541f3f125b95f346c21a7d614ea
--
2.41.0
1 year, 2 months
[nbdkit PATCH] cc: Allow configuration without absolute paths
by Eric Blake
In https://gitlab.com/nbdkit/nbdkit/-/merge_requests/30, Khem reports
that in a cross-compilation environment, nbdkit embeds the absolute
name of the cross-compiler into the resulting cc plugin, even though
running the plugin should be favoring the bare name 'cc'. This in
turn leads to non-reproducible builds. As the goal of cross-compiling
nbdkit is to produce a binary that behaves identically regardless of
the build environment used, this means we need to give the user
control over the defaults for CC and CFLAGS embedded into the cc
plugin.
However, instead of trying to munge the build environment variable as
suggested in that merge request, I found it cleaner to just add
additional precious variables to be set at configure time, as in:
./configure CC=/path/to/cross-compiler CC_PLUGIN_CC='ccache gcc' ...
Reported-by: Khem Raj
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
gitlab doesn't let me see the right email address to cc; if I can
figure that out, I'll tweak the Reported-by line as appropriate before
committing...
---
plugins/cc/nbdkit-cc-plugin.pod | 9 ++++++---
configure.ac | 11 +++++++++++
plugins/cc/Makefile.am | 4 ++--
3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/plugins/cc/nbdkit-cc-plugin.pod b/plugins/cc/nbdkit-cc-plugin.pod
index 2974890c..2bc3cfb8 100644
--- a/plugins/cc/nbdkit-cc-plugin.pod
+++ b/plugins/cc/nbdkit-cc-plugin.pod
@@ -45,9 +45,12 @@ To replace the compiler flags:
The plugin parameters C<CC>, C<CFLAGS> and C<EXTRA_CFLAGS> (written in
uppercase) can be used to control which C compiler and C compiler
-flags are used. If not set, the default compiler and flags from when
-nbdkit was itself compiled from source are used. To see what those
-were you can do:
+flags are used. If not set, you can hardcode the defaults for C<CC>
+and C<CFLAGS> at the time nbdkit is compiled from source by
+configuring with C<CC_PLUGIN_CC=...> and C<CC_PLUGIN_CFLAGS=...>,
+otherwise, the configuration for compiling nbdkit itself is used
+(C<EXTRA_CFLAGS> can only be set from the command line when starting
+the cc plugin). To see what those were you can do:
$ nbdkit cc --dump-plugin
...
diff --git a/configure.ac b/configure.ac
index afc5ddab..e5e261c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -820,6 +820,15 @@ AC_ARG_ENABLE([plugins],
[disable all bundled plugins and filters])])
AM_CONDITIONAL([HAVE_PLUGINS], [test "x$enable_plugins" != "xno"])
+dnl For the cc plugin, let the user hard-code their preferred compiler setup
+dnl Default to the settings used for nbdkit itself
+AC_ARG_VAR([CC_PLUGIN_CC],
+ [Value to use for CC when building the cc plugin, default $CC])
+: "${CC_PLUGIN_CC:=$CC}"
+AC_ARG_VAR([CC_PLUGIN_CFLAGS],
+ [Value to use for CFLAGS when building the cc plugin, default $CFLAGS])
+: "${CC_PLUGIN_CFLAGS:=$CFLAGS}"
+
dnl Check for Perl, for embedding in the perl plugin.
dnl Note that the perl binary is checked above.
AC_ARG_ENABLE([perl],
@@ -1716,6 +1725,8 @@ feature "tests using libguestfs" \
test "x$HAVE_LIBGUESTFS_TRUE" = "x" && \
test "x$USE_LIBGUESTFS_FOR_TESTS_TRUE" = "x"
feature "zlib-ng" test "x$ZLIB_NG_LIBS" != "x"
+print cc-plugin-CC "$CC_PLUGIN_CC"
+print cc-plugin-CFLAGS "$CC_PLUGIN_CFLAGS"
echo
echo "If any optional component is configured ‘no’ when you expected ‘yes’"
diff --git a/plugins/cc/Makefile.am b/plugins/cc/Makefile.am
index 935d125f..088b5ff3 100644
--- a/plugins/cc/Makefile.am
+++ b/plugins/cc/Makefile.am
@@ -45,8 +45,8 @@ nbdkit_cc_plugin_la_SOURCES = \
$(NULL)
nbdkit_cc_plugin_la_CPPFLAGS = \
- -DCC="\"$(CC)\"" \
- -DCFLAGS="\"$(CFLAGS)\"" \
+ -DCC="\"$(CC_PLUGIN_CC)\"" \
+ -DCFLAGS="\"$(CC_PLUGIN_CFLAGS)\"" \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_srcdir)/common/include \
--
2.41.0
1 year, 3 months
[libnbd PATCH v7 0/9] Rust Bindings for Libnbd
by Tage Johansson
This is the 7th version of the Rust bindings for Libnbd. It is more or
less identical to the 6th version without the already merged patches.
Best regards,
Tage
Tage Johansson (9):
rust: Make it possible to run tests with Valgrind
generator: Add information about asynchronous handle calls
generator: Add information about the lifetime of closures
rust: Use more specific closure traits
rust: async: Create an async friendly handle type
generator: Add `modifies_fd` flag to the [call] structure
rust: async: Use the modifies_fd flag to exclude calls
rust: async: Add a couple of integration tests
rust: async: Add an example
generator/API.ml | 84 ++++++
generator/API.mli | 35 +++
generator/Rust.ml | 278 +++++++++++++++++-
generator/Rust.mli | 2 +
generator/generator.ml | 2 +
rust/Cargo.toml | 6 +-
rust/Makefile.am | 5 +
rust/examples/concurrent-read-write.rs | 135 +++++++++
rust/run-tests.sh.in | 18 +-
rust/src/async_handle.rs | 268 +++++++++++++++++
rust/src/handle.rs | 2 +
rust/src/lib.rs | 8 +
rust/src/types.rs | 2 +
rust/tests/test_async_100_handle.rs | 25 ++
rust/tests/test_async_200_connect_command.rs | 33 +++
rust/tests/test_async_210_opt_abort.rs | 32 ++
rust/tests/test_async_220_opt_list.rs | 81 +++++
rust/tests/test_async_230_opt_info.rs | 122 ++++++++
rust/tests/test_async_240_opt_list_meta.rs | 147 +++++++++
.../test_async_245_opt_list_meta_queries.rs | 91 ++++++
rust/tests/test_async_250_opt_set_meta.rs | 122 ++++++++
.../test_async_255_opt_set_meta_queries.rs | 107 +++++++
rust/tests/test_async_400_pread.rs | 40 +++
rust/tests/test_async_405_pread_structured.rs | 84 ++++++
rust/tests/test_async_410_pwrite.rs | 59 ++++
rust/tests/test_async_460_block_status.rs | 92 ++++++
rust/tests/test_async_620_stats.rs | 76 +++++
scripts/git.orderfile | 1 +
28 files changed, 1934 insertions(+), 23 deletions(-)
create mode 100644 rust/examples/concurrent-read-write.rs
create mode 100644 rust/src/async_handle.rs
create mode 100644 rust/tests/test_async_100_handle.rs
create mode 100644 rust/tests/test_async_200_connect_command.rs
create mode 100644 rust/tests/test_async_210_opt_abort.rs
create mode 100644 rust/tests/test_async_220_opt_list.rs
create mode 100644 rust/tests/test_async_230_opt_info.rs
create mode 100644 rust/tests/test_async_240_opt_list_meta.rs
create mode 100644 rust/tests/test_async_245_opt_list_meta_queries.rs
create mode 100644 rust/tests/test_async_250_opt_set_meta.rs
create mode 100644 rust/tests/test_async_255_opt_set_meta_queries.rs
create mode 100644 rust/tests/test_async_400_pread.rs
create mode 100644 rust/tests/test_async_405_pread_structured.rs
create mode 100644 rust/tests/test_async_410_pwrite.rs
create mode 100644 rust/tests/test_async_460_block_status.rs
create mode 100644 rust/tests/test_async_620_stats.rs
base-commit: 33a47171653931b7e255e33930697a55eae1493b
prerequisite-patch-id: ff317be8e27608697ee070388502566ecf8546bb
prerequisite-patch-id: 6a68a5da00c78e039972118bfde68cf87d7db6af
prerequisite-patch-id: a6ae1f1d90ca8cb69d17c977428855acadd47608
prerequisite-patch-id: 053dc904d579f2d065228c1c5780109871e9cd66
prerequisite-patch-id: 99eb277dfb04af7930cc827d85fd011fc54bdd4c
prerequisite-patch-id: 0b4159540024f935140d070146d89f95b96576fa
prerequisite-patch-id: f320498a380789b51bf65b603c0627167453352c
prerequisite-patch-id: f9aea5f724ac167744aa72456340c370a43611a2
prerequisite-patch-id: 9ff0b41ad9fd00d5d67de92d574255b46cdf150a
prerequisite-patch-id: 1fd2bcd42012e5d0ab10f63a1849f2ccc706e6d6
prerequisite-patch-id: 7c5b4b59f2765e8c255857a0834805d19cecf65d
prerequisite-patch-id: 4d7bd8e07e4710e3420c1ee71502f0fd0da91ea7
prerequisite-patch-id: 5ed2a56efbc9554261f875cd299dd2b7483c78c8
prerequisite-patch-id: 52d475de3ab033859d6bd87996078ae7b3385695
prerequisite-patch-id: c6a05c89340bed6471de1d74ef95acb5b6ac2c25
prerequisite-patch-id: 097dd7285726e45b02493fc306fd3017748d50e2
prerequisite-patch-id: 359900c28144cf2059e23a2911ea9f9f9ca5db23
prerequisite-patch-id: 4db98f7b211c0de9a4095b300970e1973cf0716c
prerequisite-patch-id: 0bb320af5109c1c21e5b76d44e6ec1e7e685fd9f
prerequisite-patch-id: 205525d8ea09e77ea13f43d0720153ed5904dbcd
prerequisite-patch-id: f76cdc6ceca68268df92341985068388f25291ff
prerequisite-patch-id: 84cb140c8f0dd089ca8e9567cc2117bf38c9e558
prerequisite-patch-id: b2c3285d05fd56a258d3ec47d7d4cdcf06a57014
prerequisite-patch-id: 8938eab7a42f8a7ed82c9372be9bf29c2991787f
prerequisite-patch-id: 7694233787dd758add8c30e69965dfd1ffee7012
prerequisite-patch-id: 770993f2baac576e163abd73c2d4af3d8841b032
prerequisite-patch-id: e85690fa670dce4f6eb84b01c1b16700540789df
prerequisite-patch-id: 72e384aafbe3d1bec34371a2f4a862ff2db0b8ca
prerequisite-patch-id: 45e0dc7a06e0845f7ca992f663afcea8e9bc4ba0
prerequisite-patch-id: 0c3ac3d6f776a2e09e45534f3f74eaeb672dd003
prerequisite-patch-id: 0fc920e92dd6a36d97b4e60a68060031fe274a3b
prerequisite-patch-id: 8467ff99068b5ca29b74813ffefe1cd5689196d1
prerequisite-patch-id: cf92490b4d427b4fb036fdd5cc682a43be82d937
prerequisite-patch-id: 6518b46cd0dcfc9aec830b0532ae1fbfd3f9bbea
prerequisite-patch-id: 8e75ca2543d6e901a472b32a3f0daf81e79c090c
prerequisite-patch-id: 97ec755ec74a802d1b4efeec8be66dcdb9ac739d
prerequisite-patch-id: 95dd4160a43fa2bea8fbf614c912263cd5ad840d
prerequisite-patch-id: f219538637190d9340f30acef9b3d24125d3bb69
prerequisite-patch-id: c11ebccd6db5c175ed40b13916f9b1affd8179d8
prerequisite-patch-id: d6bcb838a1875541f3f125b95f346c21a7d614ea
--
2.41.0
1 year, 3 months