From c75b143b6caf1e671c7f3a70dd91eb20c3036ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Mon, 15 Apr 2024 21:05:52 +0200 Subject: [PATCH] C API: nix_get_string now accepts a callback to return the value --- doc/external-api/README.md | 37 +++++++++++++++++--------- src/libexpr-c/nix_api_value.cc | 12 ++++----- src/libexpr-c/nix_api_value.h | 8 +++--- tests/unit/libexpr/nix_api_expr.cc | 20 ++++++++------ tests/unit/libexpr/nix_api_external.cc | 7 ++++- tests/unit/libexpr/nix_api_value.cc | 16 +++++++---- 6 files changed, 65 insertions(+), 35 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 8a6f1c085..167c02199 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -40,24 +40,37 @@ Nix expression `builtins.nixVersion`. #include #include #include +#include +#include // NOTE: This example lacks all error handling. Production code must check for // errors, as some return values will be undefined. -int main() { - nix_libexpr_init(NULL); - Store* store = nix_store_open(NULL, "dummy://", NULL); - EvalState* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) - Value *value = nix_alloc_value(NULL, state); +void my_get_string_cb(const char * start, unsigned int n, char ** user_data) +{ + *user_data = strdup(start); +} - nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); - nix_value_force(NULL, state, value); - printf("Nix version: %s\n", nix_get_string(NULL, value)); +int main() +{ + nix_libexpr_init(NULL); - nix_gc_decref(NULL, value); - nix_state_free(state); - nix_store_free(store); - return 0; + Store * store = nix_store_open(NULL, "dummy://", NULL); + EvalState * state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) + Value * value = nix_alloc_value(NULL, state); + + nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); + nix_value_force(NULL, state, value); + + char * version; + nix_get_string(NULL, value, my_get_string_cb, version); + printf("Nix version: %s\n", version); + + free(version); + nix_gc_decref(NULL, value); + nix_state_free(state); + nix_store_free(store); + return 0; } ``` diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 817464fa8..6592591db 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -15,9 +15,9 @@ #include "value/context.hh" #ifdef HAVE_BOEHMGC -# include "gc/gc.h" -# define GC_INCLUDE_NEW 1 -# include "gc_cpp.h" +# include "gc/gc.h" +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif // Helper function to throw an exception if value is null @@ -166,16 +166,16 @@ bool nix_get_bool(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(false); } -const char * nix_get_string(nix_c_context * context, const Value * value) +nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); assert(v.type() == nix::nString); - return v.c_str(); + call_nix_get_string_callback(v.c_str(), callback, user_data); } - NIXC_CATCH_ERRS_NULL + NIXC_CATCH_ERRS } const char * nix_get_path_string(nix_c_context * context, const Value * value) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index c8e85d181..e6744e610 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -178,10 +178,13 @@ bool nix_get_bool(nix_c_context * context, const Value * value); * * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect + * @param[in] callback Called with the string value. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. * @return string - * @return NULL in case of error. + * @return error code, NIX_OK on success. */ -const char * nix_get_string(nix_c_context * context, const Value * value); +nix_err +nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data); /** @brief Get path as string * @param[out] context Optional, stores error information @@ -482,7 +485,6 @@ const StorePath * nix_realised_string_get_store_path(nix_realised_string * reali */ void nix_realised_string_free(nix_realised_string * realised_string); - // cffi end #ifdef __cplusplus } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index f5c66536d..0818f1cab 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -17,9 +17,10 @@ TEST_F(nix_api_expr_test, nix_expr_eval_from_string) { nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); nix_value_force(nullptr, state, value); - auto result = nix_get_string(nullptr, value); + std::string result; + nix_get_string(nullptr, value, OBSERVE_STRING(result)); - ASSERT_STREQ(PACKAGE_VERSION, result); + ASSERT_STREQ(PACKAGE_VERSION, result.c_str()); } TEST_F(nix_api_expr_test, nix_expr_eval_add_numbers) @@ -47,7 +48,8 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) nix_value_call(ctx, stateResult, valueFn, value, valueResult); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); - std::string p = nix_get_string(nullptr, valueResult); + std::string p; + nix_get_string(nullptr, valueResult, OBSERVE_STRING(p)); std::string pEnd = "-myname"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); @@ -69,7 +71,8 @@ TEST_F(nix_api_expr_test, nix_build_drv) nix_expr_eval_from_string(nullptr, state, expr, ".", value); Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); - const char * drvPath = nix_get_string(nullptr, drvPathValue); + std::string drvPath; + nix_get_string(nullptr, drvPathValue, OBSERVE_STRING(drvPath)); std::string p = drvPath; std::string pEnd = "-myname.drv"; @@ -78,18 +81,19 @@ TEST_F(nix_api_expr_test, nix_build_drv) // NOTE: .drvPath should be usually be ignored. Output paths are more versatile. // See https://github.com/NixOS/nix/issues/6507 // Use e.g. nix_string_realise to realise the output. - StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); + StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath.c_str()); ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); Value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); - const char * outPath = nix_get_string(ctx, outPathValue); + std::string outPath; + nix_get_string(ctx, outPathValue, OBSERVE_STRING(outPath)); p = outPath; pEnd = "-myname"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); ASSERT_EQ(true, drvStorePath->path.isDerivation()); - StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); + StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath.c_str()); ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, outStorePath)); nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); @@ -142,7 +146,7 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) }} a path: ${builtins.toFile "just-a-file" "ooh file good"} a derivation path by itself: ${ - builtins.unsafeDiscardOutputDependency + builtins.unsafeDiscardOutputDependency (derivation { name = "not-actually-built-yet"; system = builtins.currentSystem; diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 7e2caed1b..2391f8317 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -6,7 +6,9 @@ #include "nix_api_expr_internal.h" #include "nix_api_value.h" #include "nix_api_external.h" + #include "tests/nix_api_expr.hh" +#include "tests/string_callback.hh" #include @@ -58,6 +60,9 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external) nix_value_call(ctx, state, valueFn, value, valueResult); - ASSERT_STREQ("nix-external", nix_get_string(nullptr, valueResult)); + std::string string_value; + nix_get_string(nullptr, valueResult, OBSERVE_STRING(string_value)); + ASSERT_STREQ("nix-external", string_value.c_str()); } + } diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 726960638..7fbb2bbdc 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -6,6 +6,7 @@ #include "nix_api_value.h" #include "tests/nix_api_expr.hh" +#include "tests/string_callback.hh" #include #include @@ -53,13 +54,15 @@ TEST_F(nix_api_expr_test, nix_value_set_get_bool) TEST_F(nix_api_expr_test, nix_value_set_get_string) { - ASSERT_EQ(nullptr, nix_get_string(ctx, nullptr)); - ASSERT_DEATH(nix_get_string(ctx, value), ""); + std::string string_value; + ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, nullptr, OBSERVE_STRING(string_value))); + ASSERT_DEATH(nix_get_string(ctx, value, OBSERVE_STRING(string_value)), ""); const char * myString = "some string"; nix_init_string(ctx, value, myString); - ASSERT_STREQ(myString, nix_get_string(ctx, value)); + nix_get_string(ctx, value, OBSERVE_STRING(string_value)); + ASSERT_STREQ(myString, string_value.c_str()); ASSERT_STREQ("a string", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(ctx, value)); } @@ -162,11 +165,14 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) ASSERT_EQ(false, nix_has_attr_byname(ctx, value, state, "no-value")); out_value = nix_get_attr_byname(ctx, value, state, "b"); - ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); + std::string string_value; + nix_get_string(ctx, out_value, OBSERVE_STRING(string_value)); + ASSERT_STREQ("foo", string_value.c_str()); nix_gc_decref(nullptr, out_value); out_value = nix_get_attr_byidx(ctx, value, state, 1, out_name); - ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); + nix_get_string(ctx, out_value, OBSERVE_STRING(string_value)); + ASSERT_STREQ("foo", string_value.c_str()); ASSERT_STREQ("b", *out_name); nix_gc_decref(nullptr, out_value);