exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 09c8beb73e3de044f17d512b8ad75ed091bcc0e6
parent 59f53d4016cd7c6618e1820cf4ad862e3b0a8701
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Wed, 12 Nov 2025 01:21:59 +0100

TALER_JSON_spec_amount_any_array + TALER_JSON_pack_amount_array

Diffstat:
Msrc/include/taler/taler_json_lib.h | 68+++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/json/json_helper.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/json/json_pack.c | 39+++++++++++++++++++++++++++++++++++++++
Msrc/json/test_json.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 259 insertions(+), 21 deletions(-)

diff --git a/src/include/taler/taler_json_lib.h b/src/include/taler/taler_json_lib.h @@ -41,9 +41,9 @@ * @deprecated */ #define TALER_json_warn(error) \ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ - "JSON parsing failed at %s:%u: %s (%s)\n", \ - __FILE__, __LINE__, error.text, error.source) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ + "JSON parsing failed at %s:%u: %s (%s)\n", \ + __FILE__, __LINE__, error.text, error.source) /** @@ -67,8 +67,8 @@ TALER_JSON_pack_time_abs_human ( * @param ec error code to encode using canonical field names */ #define TALER_JSON_pack_ec(ec) \ - GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)), \ - GNUNET_JSON_pack_uint64 ("code", ec) + GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)), \ + GNUNET_JSON_pack_uint64 ("code", ec) /** @@ -210,6 +210,21 @@ TALER_JSON_pack_amount ( /** + * Generate packer instruction for a JSON field that contains an array of + * amounts (as strings). If @a amounts is NULL, emits JSON null. + * + * @param name field name + * @param len number of elements in @a amounts + * @param amounts array to encode + * @return json pack specification + */ +struct GNUNET_JSON_PackSpec +TALER_JSON_pack_amount_array (const char *name, + size_t len, + const struct TALER_Amount *amounts); + + +/** * Generate packer instruction for a JSON field of type * full payto. * @@ -347,6 +362,21 @@ TALER_JSON_spec_amount (const char *name, /** + * Result specification for an array of amounts. Elements must be strings in + * the usual "CUR:VAL.FRAC" notation. Allocates *@a amounts and sets + * @a amounts_len on success. Use GNUNET_JSON_parse_free() to release the array. + * + * @param field name of the field to parse + * @param amounts_len where to store the array length + * @param amounts where the allocated array pointer is written + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_amount_any_array (const char *field, + size_t *amounts_len, + struct TALER_Amount **amounts); + + +/** * Provide specification to parse given JSON object to * a currency specification. * @@ -451,10 +481,10 @@ TALER_JSON_spec_kycte (const char *name, * @param[out] dfs a `struct TALER_DenomFeeSet` to initialize */ #define TALER_JSON_SPEC_DENOM_FEES(pfx,currency,dfs) \ - TALER_JSON_spec_amount (pfx "_withdraw", (currency), &(dfs)->withdraw), \ - TALER_JSON_spec_amount (pfx "_deposit", (currency), &(dfs)->deposit), \ - TALER_JSON_spec_amount (pfx "_refresh", (currency), &(dfs)->refresh), \ - TALER_JSON_spec_amount (pfx "_refund", (currency), &(dfs)->refund) + TALER_JSON_spec_amount (pfx "_withdraw", (currency), &(dfs)->withdraw), \ + TALER_JSON_spec_amount (pfx "_deposit", (currency), &(dfs)->deposit), \ + TALER_JSON_spec_amount (pfx "_refresh", (currency), &(dfs)->refresh), \ + TALER_JSON_spec_amount (pfx "_refund", (currency), &(dfs)->refund) /** @@ -465,10 +495,10 @@ TALER_JSON_spec_kycte (const char *name, * @param dfs a `struct TALER_DenomFeeSet` to pack */ #define TALER_JSON_PACK_DENOM_FEES(pfx, dfs) \ - TALER_JSON_pack_amount (pfx "_withdraw", &(dfs)->withdraw), \ - TALER_JSON_pack_amount (pfx "_deposit", &(dfs)->deposit), \ - TALER_JSON_pack_amount (pfx "_refresh", &(dfs)->refresh), \ - TALER_JSON_pack_amount (pfx "_refund", &(dfs)->refund) + TALER_JSON_pack_amount (pfx "_withdraw", &(dfs)->withdraw), \ + TALER_JSON_pack_amount (pfx "_deposit", &(dfs)->deposit), \ + TALER_JSON_pack_amount (pfx "_refresh", &(dfs)->refresh), \ + TALER_JSON_pack_amount (pfx "_refund", &(dfs)->refund) /** @@ -478,9 +508,9 @@ TALER_JSON_spec_kycte (const char *name, * @param[out] gfs a `struct TALER_GlobalFeeSet` to initialize */ #define TALER_JSON_SPEC_GLOBAL_FEES(currency,gfs) \ - TALER_JSON_spec_amount ("history_fee", (currency), &(gfs)->history), \ - TALER_JSON_spec_amount ("account_fee", (currency), &(gfs)->account), \ - TALER_JSON_spec_amount ("purse_fee", (currency), &(gfs)->purse) + TALER_JSON_spec_amount ("history_fee", (currency), &(gfs)->history), \ + TALER_JSON_spec_amount ("account_fee", (currency), &(gfs)->account), \ + TALER_JSON_spec_amount ("purse_fee", (currency), &(gfs)->purse) /** * Macro to pack all of the global fees. @@ -488,9 +518,9 @@ TALER_JSON_spec_kycte (const char *name, * @param gfs a `struct TALER_GlobalFeeSet` to pack */ #define TALER_JSON_PACK_GLOBAL_FEES(gfs) \ - TALER_JSON_pack_amount ("history_fee", &(gfs)->history), \ - TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \ - TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse) + TALER_JSON_pack_amount ("history_fee", &(gfs)->history), \ + TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \ + TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse) /** diff --git a/src/json/json_helper.c b/src/json/json_helper.c @@ -149,6 +149,124 @@ TALER_JSON_spec_amount_any (const char *name, /** + * Closure for parsing amount arrays. + */ +struct AmountArrayCtx +{ + /** + * Pointer where to store the resulting array length. + */ + size_t *len; +}; + + +/** + * Parse a JSON array of arbitrary amounts. + */ +static enum GNUNET_GenericReturnValue +parse_amount_any_array (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct AmountArrayCtx *ctx = cls; + struct TALER_Amount **out = spec->ptr; + + GNUNET_assert (NULL != ctx); + GNUNET_assert (NULL != out); + *out = NULL; + if (NULL != ctx->len) + *ctx->len = 0; + + if (! json_is_array (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + { + size_t len = json_array_size (root); + + if (NULL != ctx->len) + *ctx->len = len; + if (0 == len) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *out = GNUNET_new_array (len, + struct TALER_Amount); + json_t *entry; + size_t idx; + + json_array_foreach (root, idx, entry) { + const char *amount_str; + + if (! json_is_string (entry)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + amount_str = json_string_value (entry); + if (GNUNET_OK != + TALER_string_to_amount (amount_str, + &(*out)[idx])) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + } + return GNUNET_OK; +} + + +/** + * Cleanup helper for the amount array parser. + */ +static void +clean_amount_any_array (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct AmountArrayCtx *ctx = cls; + + if (NULL != spec->ptr) + { + GNUNET_free (*(void **) spec->ptr); + *(void **) spec->ptr = NULL; + } + if ( (NULL != ctx) && + (NULL != ctx->len) ) + *ctx->len = 0; + GNUNET_free (ctx); +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_amount_any_array (const char *field, + size_t *amounts_len, + struct TALER_Amount **amounts) +{ + struct AmountArrayCtx *ctx; + + GNUNET_assert (NULL != amounts_len); + GNUNET_assert (NULL != amounts); + ctx = GNUNET_new (struct AmountArrayCtx); + ctx->len = amounts_len; + { + struct GNUNET_JSON_Specification ret = { + .parser = &parse_amount_any_array, + .cleaner = &clean_amount_any_array, + .cls = ctx, + .field = field, + .ptr = amounts + }; + + return ret; + } +} + + +/** * Parse given JSON object to currency spec. * * @param cls closure, NULL @@ -1911,7 +2029,7 @@ parse_array_fixed (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - ptr+=entry_size; + ptr += entry_size; } } return GNUNET_OK; @@ -2011,7 +2129,7 @@ parse_array_of_data (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - ptr+=info->entry_size; + ptr += info->entry_size; } } return GNUNET_OK; diff --git a/src/json/json_pack.c b/src/json/json_pack.c @@ -441,6 +441,45 @@ TALER_JSON_pack_amount (const char *name, struct GNUNET_JSON_PackSpec +TALER_JSON_pack_amount_array (const char *name, + size_t len, + const struct TALER_Amount *amounts) +{ + struct GNUNET_JSON_PackSpec ps = { + .field_name = name, + }; + + if (NULL == amounts) + { + ps.object = json_null (); + return ps; + } + { + json_t *array = json_array (); + + GNUNET_assert (NULL != array); + for (size_t i = 0; i<len; i++) + { + json_t *entry; + + char *amount_str = TALER_amount_to_string (&amounts[i]); + GNUNET_assert (NULL != amount_str); + + entry = json_string (amount_str); + + GNUNET_free (amount_str); + GNUNET_assert (NULL != entry); + GNUNET_assert (0 == + json_array_append_new (array, + entry)); + } + ps.object = array; + } + return ps; +} + + +struct GNUNET_JSON_PackSpec TALER_JSON_pack_full_payto ( const char *name, const struct TALER_FullPayto payto) diff --git a/src/json/test_json.c b/src/json/test_json.c @@ -58,6 +58,55 @@ test_amount (void) } +/** + * Verify JSON packing/parsing for amount arrays. + * + * @return 0 on success + */ +static int +test_amount_array (void) +{ + struct TALER_Amount amounts[2]; + struct TALER_Amount *parsed = NULL; + size_t parsed_len = 0; + struct GNUNET_JSON_Specification spec[2]; + json_t *doc; + const size_t num_amounts = sizeof (amounts) / sizeof (amounts[0]); + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.2", + &amounts[0])); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:3.4", + &amounts[1])); + + spec[0] = TALER_JSON_spec_amount_any_array ("amounts", + &parsed_len, + &parsed); + spec[1] = GNUNET_JSON_spec_end (); + + doc = GNUNET_JSON_PACK ( + TALER_JSON_pack_amount_array ("amounts", + num_amounts, + amounts)); + GNUNET_assert (NULL != doc); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (doc, + spec, + NULL, + NULL)); + GNUNET_assert (parsed_len == num_amounts); + for (size_t i = 0; i<num_amounts; i++) + GNUNET_assert (0 == + TALER_amount_cmp (&amounts[i], + &parsed[i])); + GNUNET_JSON_parse_free (spec); + json_decref (doc); + + return 0; +} + + struct TestPath_Closure { const char **object_ids; @@ -474,6 +523,8 @@ main (int argc, NULL); if (0 != test_amount ()) return 1; + if (0 != test_amount_array ()) + return 1; if (0 != test_contract ()) return 2; if (0 != test_json_canon ())