From bfec2d700b8b0ccf6669e0408b5c5c75299ef47e Mon Sep 17 00:00:00 2001 From: Binbin Date: Wed, 12 Apr 2023 15:11:29 +0800 Subject: [PATCH] Add RM_ReplyWithErrorFormat that can support format (#11923) * Add RM_ReplyWithErrorFormat that can support format Reply with the error create from a printf format and arguments. If the error code is already passed in the string 'fmt', the error code provided is used, otherwise the string "-ERR " for the generic error code is automatically added. The usage is, for example: RedisModule_ReplyWithErrorFormat(ctx, "An error: %s", "foo"); RedisModule_ReplyWithErrorFormat(ctx, "-WRONGTYPE Wrong Type: %s", "foo"); The function always returns REDISMODULE_OK. --- src/module.c | 27 +++++++++++++++++++++++++++ src/networking.c | 4 ++-- src/redismodule.h | 2 ++ src/server.h | 1 + tests/modules/reply.c | 10 ++++++++++ tests/unit/moduleapi/reply.tcl | 11 +++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/module.c b/src/module.c index e29277956..2197111c2 100644 --- a/src/module.c +++ b/src/module.c @@ -2996,6 +2996,32 @@ int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) { return REDISMODULE_OK; } +/* Reply with the error create from a printf format and arguments. + * + * If the error code is already passed in the string 'fmt', the error + * code provided is used, otherwise the string "-ERR " for the generic + * error code is automatically added. + * + * The usage is, for example: + * + * RedisModule_ReplyWithErrorFormat(ctx, "An error: %s", "foo"); + * + * RedisModule_ReplyWithErrorFormat(ctx, "-WRONGTYPE Wrong Type: %s", "foo"); + * + * The function always returns REDISMODULE_OK. + */ +int RM_ReplyWithErrorFormat(RedisModuleCtx *ctx, const char *fmt, ...) { + client *c = moduleGetReplyClient(ctx); + if (c == NULL) return REDISMODULE_OK; + + va_list ap; + va_start(ap, fmt); + addReplyErrorFormatInternal(c, 0, fmt, ap); + va_end(ap); + + return REDISMODULE_OK; +} + /* Reply with a simple string (`+... \r\n` in RESP protocol). This replies * are suitable only when sending a small non-binary string with small * overhead, like "OK" or similar replies. @@ -13438,6 +13464,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(WrongArity); REGISTER_API(ReplyWithLongLong); REGISTER_API(ReplyWithError); + REGISTER_API(ReplyWithErrorFormat); REGISTER_API(ReplyWithSimpleString); REGISTER_API(ReplyWithArray); REGISTER_API(ReplyWithMap); diff --git a/src/networking.c b/src/networking.c index 7fa18af48..574773b37 100644 --- a/src/networking.c +++ b/src/networking.c @@ -620,9 +620,9 @@ void addReplyErrorSdsSafe(client *c, sds err) { addReplyErrorSdsEx(c, err, 0); } -/* Internal function used by addReplyErrorFormat and addReplyErrorFormatEx. +/* Internal function used by addReplyErrorFormat, addReplyErrorFormatEx and RM_ReplyWithErrorFormat. * Refer to afterErrorReply for more information about the flags. */ -static void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap) { +void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap) { va_list cpy; va_copy(cpy,ap); sds s = sdscatvprintf(sdsempty(),fmt,cpy); diff --git a/src/redismodule.h b/src/redismodule.h index f58da2f49..fff6ac9bc 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -1014,6 +1014,7 @@ REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModul REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR; REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR; +REDISMODULE_API int (*RedisModule_ReplyWithErrorFormat)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; REDISMODULE_API int (*RedisModule_ReplyWithMap)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR; @@ -1332,6 +1333,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(WrongArity); REDISMODULE_GET_API(ReplyWithLongLong); REDISMODULE_GET_API(ReplyWithError); + REDISMODULE_GET_API(ReplyWithErrorFormat); REDISMODULE_GET_API(ReplyWithSimpleString); REDISMODULE_GET_API(ReplyWithArray); REDISMODULE_GET_API(ReplyWithMap); diff --git a/src/server.h b/src/server.h index 02ec91ee0..b286b6803 100644 --- a/src/server.h +++ b/src/server.h @@ -2553,6 +2553,7 @@ void setDeferredReplyBulkSds(client *c, void *node, sds s); void addReplyErrorObject(client *c, robj *err); void addReplyOrErrorObject(client *c, robj *reply); void afterErrorReply(client *c, const char *s, size_t len, int flags); +void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap); void addReplyErrorSdsEx(client *c, sds err, int flags); void addReplyErrorSds(client *c, sds err); void addReplyErrorSdsSafe(client *c, sds err); diff --git a/tests/modules/reply.c b/tests/modules/reply.c index f890560e0..c5baa6635 100644 --- a/tests/modules/reply.c +++ b/tests/modules/reply.c @@ -156,6 +156,14 @@ int rw_error(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { return RedisModule_ReplyWithError(ctx, "An error"); } +int rw_error_format(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + if (argc != 3) return RedisModule_WrongArity(ctx); + + return RedisModule_ReplyWithErrorFormat(ctx, + RedisModule_StringPtrLen(argv[1], NULL), + RedisModule_StringPtrLen(argv[2], NULL)); +} + int rw_verbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); @@ -197,6 +205,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"rw.error",rw_error,"",0,0,0) != REDISMODULE_OK) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"rw.error_format",rw_error_format,"",0,0,0) != REDISMODULE_OK) + return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"rw.verbatim",rw_verbatim,"",0,0,0) != REDISMODULE_OK) return REDISMODULE_ERR; diff --git a/tests/unit/moduleapi/reply.tcl b/tests/unit/moduleapi/reply.tcl index 291253d3c..547be21c0 100644 --- a/tests/unit/moduleapi/reply.tcl +++ b/tests/unit/moduleapi/reply.tcl @@ -126,6 +126,17 @@ start_server {tags {"modules"}} { assert_match "An error" $e } + test "RESP$proto: RM_ReplyWithErrorFormat: error format reply" { + catch {r rw.error_format "An error: %s" foo} e + assert_match "ERR An error: foo" $e + + catch {r rw.error_format "-ERR An error: %s" foo2} e + assert_match "ERR An error: foo2" $e + + catch {r rw.error_format "-WRONGTYPE A type error: %s" foo3} e + assert_match "WRONGTYPE A type error: foo3" $e + } + r hello 2 }