mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 05:56:15 +02:00
C API: Add nix_init_apply
Thunks are relevant when initializing attrsets and lists, passing arguments. This is an important way to produce them.
This commit is contained in:
parent
05b9dac754
commit
ad643cde58
4 changed files with 157 additions and 2 deletions
|
@ -93,6 +93,8 @@ nix_err nix_expr_eval_from_string(
|
|||
* @param[in] arg The argument to pass to the function.
|
||||
* @param[out] value The result of the function call.
|
||||
* @return NIX_OK if the function call was successful, an error code otherwise.
|
||||
* @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk.
|
||||
* Note the different argument order.
|
||||
*/
|
||||
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value);
|
||||
|
||||
|
|
|
@ -404,6 +404,19 @@ nix_err nix_init_null(nix_c_context * context, Value * value)
|
|||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto & v = check_value_not_null(value);
|
||||
auto & f = check_value_not_null(fn);
|
||||
auto & a = check_value_not_null(arg);
|
||||
v.mkApp(&f, &a);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val)
|
||||
{
|
||||
if (context)
|
||||
|
|
|
@ -342,8 +342,24 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i);
|
|||
* @param[out] value Nix value to modify
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
|
||||
nix_err nix_init_null(nix_c_context * context, Value * value);
|
||||
|
||||
/** @brief Set the value to a thunk that will perform a function application when needed.
|
||||
*
|
||||
* Thunks may be put into attribute sets and lists to perform some computation lazily; on demand.
|
||||
* However, note that in some places, a thunk must not be returned, such as in the return value of a PrimOp.
|
||||
* In such cases, you may use nix_value_call() instead (but note the different argument order).
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[out] value Nix value to modify
|
||||
* @param[in] fn function to call
|
||||
* @param[in] arg argument to pass
|
||||
* @return error code, NIX_OK on successful initialization.
|
||||
* @see nix_value_call() for a similar function that performs the call immediately and only stores the return value.
|
||||
* Note the different argument order.
|
||||
*/
|
||||
nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg);
|
||||
|
||||
/** @brief Set an external value
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[out] value Nix value to modify
|
||||
|
@ -421,7 +437,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState *
|
|||
/** @brief Insert bindings into a builder
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] builder BindingsBuilder to insert into
|
||||
* @param[in] name attribute name, copied into the symbol store
|
||||
* @param[in] name attribute name, only used for the duration of the call.
|
||||
* @param[in] value value to give the binding
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "tests/nix_api_expr.hh"
|
||||
#include "tests/string_callback.hh"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include <cstdlib>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
@ -187,4 +188,127 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr)
|
|||
free(out_name);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_init)
|
||||
{
|
||||
// Setup
|
||||
|
||||
// two = 2;
|
||||
// f = a: a * a;
|
||||
|
||||
Value * two = nix_alloc_value(ctx, state);
|
||||
nix_init_int(ctx, two, 2);
|
||||
|
||||
Value * f = nix_alloc_value(ctx, state);
|
||||
nix_expr_eval_from_string(
|
||||
ctx, state, R"(
|
||||
a: a * a
|
||||
)",
|
||||
"<test>", f);
|
||||
|
||||
// Test
|
||||
|
||||
// r = f two;
|
||||
|
||||
Value * r = nix_alloc_value(ctx, state);
|
||||
nix_init_apply(ctx, r, f, two);
|
||||
assert_ctx_ok();
|
||||
|
||||
ValueType t = nix_get_type(ctx, r);
|
||||
assert_ctx_ok();
|
||||
|
||||
ASSERT_EQ(t, NIX_TYPE_THUNK);
|
||||
|
||||
nix_value_force(ctx, state, r);
|
||||
|
||||
t = nix_get_type(ctx, r);
|
||||
assert_ctx_ok();
|
||||
|
||||
ASSERT_EQ(t, NIX_TYPE_INT);
|
||||
|
||||
int n = nix_get_int(ctx, r);
|
||||
assert_ctx_ok();
|
||||
|
||||
ASSERT_EQ(n, 4);
|
||||
|
||||
// Clean up
|
||||
nix_gc_decref(ctx, two);
|
||||
nix_gc_decref(ctx, f);
|
||||
nix_gc_decref(ctx, r);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_init_apply_error)
|
||||
{
|
||||
Value * some_string = nix_alloc_value(ctx, state);
|
||||
nix_init_string(ctx, some_string, "some string");
|
||||
assert_ctx_ok();
|
||||
|
||||
Value * v = nix_alloc_value(ctx, state);
|
||||
nix_init_apply(ctx, v, some_string, some_string);
|
||||
assert_ctx_ok();
|
||||
|
||||
// All ok. Call has not been evaluated yet.
|
||||
|
||||
// Evaluate it
|
||||
nix_value_force(ctx, state, v);
|
||||
ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR);
|
||||
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("attempt to call something which is not a function but"));
|
||||
|
||||
// Clean up
|
||||
nix_gc_decref(ctx, some_string);
|
||||
nix_gc_decref(ctx, v);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg)
|
||||
{
|
||||
// f is a lazy function: it does not evaluate its argument before returning its return value
|
||||
// g is a helper to produce e
|
||||
// e is a thunk that throws an exception
|
||||
//
|
||||
// r = f e
|
||||
// r should not throw an exception, because e is not evaluated
|
||||
|
||||
Value * f = nix_alloc_value(ctx, state);
|
||||
nix_expr_eval_from_string(
|
||||
ctx, state, R"(
|
||||
a: { foo = a; }
|
||||
)",
|
||||
"<test>", f);
|
||||
assert_ctx_ok();
|
||||
|
||||
Value * e = nix_alloc_value(ctx, state);
|
||||
{
|
||||
Value * g = nix_alloc_value(ctx, state);
|
||||
nix_expr_eval_from_string(
|
||||
ctx, state, R"(
|
||||
_ignore: throw "error message for test case nix_value_init_apply_lazy_arg"
|
||||
)",
|
||||
"<test>", g);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_init_apply(ctx, e, g, g);
|
||||
assert_ctx_ok();
|
||||
nix_gc_decref(ctx, g);
|
||||
}
|
||||
|
||||
Value * r = nix_alloc_value(ctx, state);
|
||||
nix_init_apply(ctx, r, f, e);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value_force(ctx, state, r);
|
||||
assert_ctx_ok();
|
||||
|
||||
auto n = nix_get_attrs_size(ctx, r);
|
||||
assert_ctx_ok();
|
||||
ASSERT_EQ(1, n);
|
||||
|
||||
// nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception
|
||||
Value * foo = nix_get_attr_byname(ctx, r, state, "foo");
|
||||
ASSERT_EQ(nullptr, foo);
|
||||
ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg"));
|
||||
|
||||
// Clean up
|
||||
nix_gc_decref(ctx, f);
|
||||
nix_gc_decref(ctx, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue