C api: nix_export_std_string -> nix_observe_string

This commit is contained in:
José Luis Lafuente 2024-03-27 17:50:36 +01:00
parent 940ff6535c
commit d96b52bd8b
No known key found for this signature in database
GPG key ID: 8A3455EBE455489A
8 changed files with 92 additions and 76 deletions

View file

@ -56,26 +56,24 @@ void nix_store_free(Store * store)
delete store; delete store;
} }
nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n) nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
auto res = store->ptr->getUri(); auto res = store->ptr->getUri();
return nix_export_std_string(context, res, dest, n); return call_nix_observe_string(res, callback, user_data);
} }
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
} }
nix_err nix_store_get_version(nix_c_context * context, Store * store, char * dest, unsigned int n) nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
auto res = store->ptr->getVersion(); auto res = store->ptr->getVersion();
if (!res) return call_nix_observe_string(res.value_or(""), callback, user_data);
res = "";
return nix_export_std_string(context, *res, dest, n);
} }
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
} }

View file

@ -71,11 +71,12 @@ void nix_store_free(Store * store);
* @brief get the URI of a nix store * @brief get the URI of a nix store
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] store nix store reference * @param[in] store nix store reference
* @param[out] dest The allocated area to write the string to. * @param[in] callback Called with the URI.
* @param[in] n Maximum size of the returned string. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @see nix_observe_string
* @return error code, NIX_OK on success. * @return error code, NIX_OK on success.
*/ */
nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n); nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data);
// returns: owned StorePath* // returns: owned StorePath*
/** /**
@ -130,11 +131,12 @@ nix_err nix_store_realise(
* If the store doesn't have a version (like the dummy store), returns an empty string. * If the store doesn't have a version (like the dummy store), returns an empty string.
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] store nix store reference * @param[in] store nix store reference
* @param[out] dest The allocated area to write the string to. * @param[in] callback Called with the version.
* @param[in] n Maximum size of the returned string. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @see nix_observe_string
* @return error code, NIX_OK on success. * @return error code, NIX_OK on success.
*/ */
nix_err nix_store_get_version(nix_c_context *, Store * store, char * dest, unsigned int n); nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data);
// cffi end // cffi end
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -63,16 +63,17 @@ const char * nix_version_get()
} }
// Implementations // Implementations
nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n)
nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
std::map<std::string, nix::AbstractConfig::SettingInfo> settings; std::map<std::string, nix::AbstractConfig::SettingInfo> settings;
nix::globalConfig.getSettings(settings); nix::globalConfig.getSettings(settings);
if (settings.contains(key)) if (settings.contains(key)) {
return nix_export_std_string(context, settings[key].value, value, n); return call_nix_observe_string(settings[key].value, callback, user_data);
else { } else {
return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found");
} }
} }
@ -114,24 +115,24 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con
return nullptr; return nullptr;
} }
nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n) nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { if (read_context->last_err_code != NIX_ERR_NIX_ERROR) {
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error");
} }
return nix_export_std_string(context, read_context->name, value, n); return call_nix_observe_string(read_context->name, callback, user_data);
} }
nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n) nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { if (read_context->last_err_code != NIX_ERR_NIX_ERROR) {
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error");
} }
return nix_export_std_string(context, read_context->info->msg.str(), value, n); return call_nix_observe_string(read_context->info->msg.str(), callback, user_data);
} }
nix_err nix_err_code(const nix_c_context * read_context) nix_err nix_err_code(const nix_c_context * read_context)
@ -140,12 +141,8 @@ nix_err nix_err_code(const nix_c_context * read_context)
} }
// internal // internal
nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n) nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data)
{ {
size_t i = str.copy(dest, n - 1); ((nix_observe_string) callback)(str.c_str(), str.size(), user_data);
dest[i] = 0; return NIX_OK;
if (i == n - 1) {
return nix_set_err_msg(context, NIX_ERR_OVERFLOW, "Provided buffer too short");
} else
return NIX_OK;
} }

View file

@ -119,6 +119,15 @@ typedef int nix_err;
*/ */
typedef struct nix_c_context nix_c_context; typedef struct nix_c_context nix_c_context;
/**
* @brief Called to get the value of a string owned by Nix.
*
* @param[in] start the string to copy.
* @param[in] n the string length.
* @param[in] user_data optional, arbitrary data, passed to the nix_observe_string when it's called.
*/
typedef void (*nix_observe_string)(const char * start, unsigned int n, void * user_data);
// Function prototypes // Function prototypes
/** /**
@ -160,14 +169,13 @@ nix_err nix_libutil_init(nix_c_context * context);
* *
* @param[out] context optional, Stores error information * @param[out] context optional, Stores error information
* @param[in] key The key of the setting to retrieve. * @param[in] key The key of the setting to retrieve.
* @param[out] value A pointer to a buffer where the value of the setting will * @param[in] callback Called with the setting value.
* be stored. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @param[in] n The size of the buffer pointed to by value. * @see nix_observe_string
* @return NIX_ERR_KEY if the setting is unknown, NIX_ERR_OVERFLOW if the * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved
* provided buffer is too short, or NIX_OK if the setting was retrieved
* successfully. * successfully.
*/ */
nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n); nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data);
/** /**
* @brief Sets a setting in the nix global configuration. * @brief Sets a setting in the nix global configuration.
@ -227,12 +235,14 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns
* *
* @param[out] context optional, the context to store errors in if this function * @param[out] context optional, the context to store errors in if this function
* fails * fails
* @param[in] read_context the context to retrieve the error message from * @param[in] read_context the context to retrieve the error message from.
* @param[out] value The allocated area to write the error string to. * @param[in] callback Called with the error message.
* @param[in] n Maximum size of the returned string. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @see nix_observe_string
* @return NIX_OK if there were no errors, an error code otherwise. * @return NIX_OK if there were no errors, an error code otherwise.
*/ */
nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n); nix_err
nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data);
/** /**
* @brief Retrieves the error name from a context. * @brief Retrieves the error name from a context.
@ -245,11 +255,12 @@ nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_con
* @param context optional, the context to store errors in if this function * @param context optional, the context to store errors in if this function
* fails * fails
* @param[in] read_context the context to retrieve the error message from * @param[in] read_context the context to retrieve the error message from
* @param[out] value The allocated area to write the error string to. * @param[in] callback Called with the error name.
* @param[in] n Maximum size of the returned string. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
* @see nix_observe_string
* @return NIX_OK if there were no errors, an error code otherwise. * @return NIX_OK if there were no errors, an error code otherwise.
*/ */
nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n); nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data);
/** /**
* @brief Retrieves the most recent error code from a nix_c_context * @brief Retrieves the most recent error code from a nix_c_context

View file

@ -20,16 +20,16 @@ nix_err nix_context_error(nix_c_context * context);
/** /**
* Internal use only. * Internal use only.
* *
* Export a std::string across the C api boundary * Helper to invoke nix_observe_string
* @param context optional, the context to store errors in if this function * @param context optional, the context to store errors in if this function
* fails * fails
* @param str The string to export * @param str The string to observe
* @param value The allocated area to write the string to. * @param callback Called with the observed string.
* @param n Maximum size of the returned string. * @param user_data optional, arbitrary data, passed to the callback when it's called.
* @return NIX_OK if there were no errors, NIX_ERR_OVERFLOW if the string length * @return NIX_OK if there were no errors.
* exceeds `n`. * @see nix_observe_string
*/ */
nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n); nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data);
#define NIXC_CATCH_ERRS \ #define NIXC_CATCH_ERRS \
catch (...) \ catch (...) \

View file

@ -7,6 +7,11 @@
namespace nixC { namespace nixC {
void observe_string_cb(const char * start, unsigned int n, std::string * user_data)
{
*user_data = std::string(start);
}
std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name"; std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name";
TEST_F(nix_api_util_context, nix_libstore_init) TEST_F(nix_api_util_context, nix_libstore_init)
@ -17,10 +22,10 @@ TEST_F(nix_api_util_context, nix_libstore_init)
TEST_F(nix_api_store_test, nix_store_get_uri) TEST_F(nix_api_store_test, nix_store_get_uri)
{ {
char value[256]; std::string str;
auto ret = nix_store_get_uri(ctx, store, value, 256); auto ret = nix_store_get_uri(ctx, store, (void *) observe_string_cb, &str);
ASSERT_EQ(NIX_OK, ret); ASSERT_EQ(NIX_OK, ret);
ASSERT_STREQ("local", value); ASSERT_STREQ("local", str.c_str());
} }
TEST_F(nix_api_store_test, InvalidPathFails) TEST_F(nix_api_store_test, InvalidPathFails)
@ -50,10 +55,10 @@ TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull)
TEST_F(nix_api_store_test, get_version) TEST_F(nix_api_store_test, get_version)
{ {
char value[256]; std::string str;
auto ret = nix_store_get_version(ctx, store, value, 256); auto ret = nix_store_get_version(ctx, store, (void *) observe_string_cb, &str);
ASSERT_EQ(NIX_OK, ret); ASSERT_EQ(NIX_OK, ret);
ASSERT_STREQ(PACKAGE_VERSION, value); ASSERT_STREQ(PACKAGE_VERSION, str.c_str());
} }
TEST_F(nix_api_util_context, nix_store_open_dummy) TEST_F(nix_api_util_context, nix_store_open_dummy)
@ -63,9 +68,9 @@ TEST_F(nix_api_util_context, nix_store_open_dummy)
ASSERT_EQ(NIX_OK, ctx->last_err_code); ASSERT_EQ(NIX_OK, ctx->last_err_code);
ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); ASSERT_STREQ("dummy", store->ptr->getUri().c_str());
char value[256]; std::string str;
nix_store_get_version(ctx, store, value, 256); nix_store_get_version(ctx, store, (void *) observe_string_cb, &str);
ASSERT_STREQ("", value); ASSERT_STREQ("", str.c_str());
nix_store_free(store); nix_store_free(store);
} }

View file

@ -5,6 +5,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace nixC { namespace nixC {
class nix_api_util_context : public ::testing::Test class nix_api_util_context : public ::testing::Test
{ {
protected: protected:

View file

@ -9,6 +9,11 @@
namespace nixC { namespace nixC {
void observe_string_cb(const char * start, unsigned int n, std::string * user_data)
{
*user_data = std::string(start);
}
TEST_F(nix_api_util_context, nix_context_error) TEST_F(nix_api_util_context, nix_context_error)
{ {
std::string err_msg_ref; std::string err_msg_ref;
@ -57,13 +62,13 @@ static nix::GlobalConfig::Register rs(&mySettings);
TEST_F(nix_api_util_context, nix_setting_get) TEST_F(nix_api_util_context, nix_setting_get)
{ {
ASSERT_EQ(ctx->last_err_code, NIX_OK); ASSERT_EQ(ctx->last_err_code, NIX_OK);
char value[256]; std::string setting_value;
nix_err result = nix_setting_get(ctx, "invalid-key", value, 256); nix_err result = nix_setting_get(ctx, "invalid-key", (void *) observe_string_cb, &setting_value);
ASSERT_EQ(result, NIX_ERR_KEY); ASSERT_EQ(result, NIX_ERR_KEY);
result = nix_setting_get(ctx, "setting-name", value, 256); result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value);
ASSERT_EQ(result, NIX_OK); ASSERT_EQ(result, NIX_OK);
ASSERT_STREQ("empty", value); ASSERT_STREQ("empty", setting_value.c_str());
} }
TEST_F(nix_api_util_context, nix_setting_set) TEST_F(nix_api_util_context, nix_setting_set)
@ -74,10 +79,10 @@ TEST_F(nix_api_util_context, nix_setting_set)
result = nix_setting_set(ctx, "setting-name", "new-value"); result = nix_setting_set(ctx, "setting-name", "new-value");
ASSERT_EQ(result, NIX_OK); ASSERT_EQ(result, NIX_OK);
char value[256]; std::string setting_value;
result = nix_setting_get(ctx, "setting-name", value, 256); result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value);
ASSERT_EQ(result, NIX_OK); ASSERT_EQ(result, NIX_OK);
ASSERT_STREQ("new-value", value); ASSERT_STREQ("new-value", setting_value.c_str());
} }
TEST_F(nix_api_util_context, nix_err_msg) TEST_F(nix_api_util_context, nix_err_msg)
@ -100,26 +105,26 @@ TEST_F(nix_api_util_context, nix_err_msg)
TEST_F(nix_api_util_context, nix_err_info_msg) TEST_F(nix_api_util_context, nix_err_info_msg)
{ {
std::string err_info;
// no error // no error
EXPECT_THROW(nix_err_info_msg(NULL, ctx, NULL, 256), nix::Error); EXPECT_THROW(nix_err_info_msg(NULL, ctx, (void *) observe_string_cb, &err_info), nix::Error);
try { try {
throw nix::Error("testing error"); throw nix::Error("testing error");
} catch (...) { } catch (...) {
nix_context_error(ctx); nix_context_error(ctx);
} }
char buf[256]; nix_err_info_msg(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_info);
nix_err_info_msg(nix_c_context_create(), ctx, buf, 256); ASSERT_STREQ("testing error", err_info.c_str());
ASSERT_EQ(std::string(buf), "testing error");
// should overflow
EXPECT_THROW(nix_err_info_msg(NULL, ctx, buf, 1), nix::Error);
} }
TEST_F(nix_api_util_context, nix_err_name) TEST_F(nix_api_util_context, nix_err_name)
{ {
std::string err_name;
// no error // no error
EXPECT_THROW(nix_err_name(NULL, ctx, NULL, 256), nix::Error); EXPECT_THROW(nix_err_name(NULL, ctx, (void *) observe_string_cb, &err_name), nix::Error);
std::string err_msg_ref; std::string err_msg_ref;
try { try {
@ -127,12 +132,8 @@ TEST_F(nix_api_util_context, nix_err_name)
} catch (...) { } catch (...) {
nix_context_error(ctx); nix_context_error(ctx);
} }
char err_name[32]; nix_err_name(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_name);
nix_err_name(nix_c_context_create(), ctx, err_name, 32);
ASSERT_EQ(std::string(err_name), "nix::Error"); ASSERT_EQ(std::string(err_name), "nix::Error");
// overflow
EXPECT_THROW(nix_err_name(NULL, ctx, err_name, 1), nix::Error);
} }
TEST_F(nix_api_util_context, nix_err_code) TEST_F(nix_api_util_context, nix_err_code)
@ -141,4 +142,5 @@ TEST_F(nix_api_util_context, nix_err_code)
nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error");
ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN); ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN);
} }
} }