// Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
// SPDX-FileCopyrightText: 2024 Redict Contributors
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
//
// SPDX-License-Identifier: BSD-3-Clause

#include "../redictmodule.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

/* HELLO.SIMPLE is among the simplest commands you can implement.
 * It just returns the currently selected DB id, a functionality which is
 * missing in Redict. The command uses two important API calls: one to
 * fetch the currently selected DB, the other in order to send the client
 * an integer reply as response. */
int HelloSimple_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    REDICTMODULE_NOT_USED(argv);
    REDICTMODULE_NOT_USED(argc);
    RedictModule_ReplyWithLongLong(ctx,RedictModule_GetSelectedDb(ctx));
    return REDICTMODULE_OK;
}

/* HELLO.PUSH.NATIVE re-implements RPUSH, and shows the low level modules API
 * where you can "open" keys, make low level operations, create new keys by
 * pushing elements into non-existing keys, and so forth.
 *
 * You'll find this command to be roughly as fast as the actual RPUSH
 * command. */
int HelloPushNative_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc)
{
    if (argc != 3) return RedictModule_WrongArity(ctx);

    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);

    RedictModule_ListPush(key,REDICTMODULE_LIST_TAIL,argv[2]);
    size_t newlen = RedictModule_ValueLength(key);
    RedictModule_CloseKey(key);
    RedictModule_ReplyWithLongLong(ctx,newlen);
    return REDICTMODULE_OK;
}

/* HELLO.PUSH.CALL implements RPUSH using an higher level approach, calling
 * a Redict command instead of working with the key in a low level way. This
 * approach is useful when you need to call Redict commands that are not
 * available as low level APIs, or when you don't need the maximum speed
 * possible but instead prefer implementation simplicity. */
int HelloPushCall_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc)
{
    if (argc != 3) return RedictModule_WrongArity(ctx);

    RedictModuleCallReply *reply;

    reply = RedictModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
    long long len = RedictModule_CallReplyInteger(reply);
    RedictModule_FreeCallReply(reply);
    RedictModule_ReplyWithLongLong(ctx,len);
    return REDICTMODULE_OK;
}

/* HELLO.PUSH.CALL2
 * This is exactly as HELLO.PUSH.CALL, but shows how we can reply to the
 * client using directly a reply object that Call() returned. */
int HelloPushCall2_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc)
{
    if (argc != 3) return RedictModule_WrongArity(ctx);

    RedictModuleCallReply *reply;

    reply = RedictModule_Call(ctx,"RPUSH","ss",argv[1],argv[2]);
    RedictModule_ReplyWithCallReply(ctx,reply);
    RedictModule_FreeCallReply(reply);
    return REDICTMODULE_OK;
}

/* HELLO.LIST.SUM.LEN returns the total length of all the items inside
 * a Redict list, by using the high level Call() API.
 * This command is an example of the array reply access. */
int HelloListSumLen_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc)
{
    if (argc != 2) return RedictModule_WrongArity(ctx);

    RedictModuleCallReply *reply;

    reply = RedictModule_Call(ctx,"LRANGE","sll",argv[1],(long long)0,(long long)-1);
    size_t strlen = 0;
    size_t items = RedictModule_CallReplyLength(reply);
    size_t j;
    for (j = 0; j < items; j++) {
        RedictModuleCallReply *ele = RedictModule_CallReplyArrayElement(reply,j);
        strlen += RedictModule_CallReplyLength(ele);
    }
    RedictModule_FreeCallReply(reply);
    RedictModule_ReplyWithLongLong(ctx,strlen);
    return REDICTMODULE_OK;
}

/* HELLO.LIST.SPLICE srclist dstlist count
 * Moves 'count' elements from the tail of 'srclist' to the head of
 * 'dstlist'. If less than count elements are available, it moves as much
 * elements as possible. */
int HelloListSplice_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 4) return RedictModule_WrongArity(ctx);

    RedictModuleKey *srckey = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    RedictModuleKey *dstkey = RedictModule_OpenKey(ctx,argv[2],
        REDICTMODULE_READ|REDICTMODULE_WRITE);

    /* Src and dst key must be empty or lists. */
    if ((RedictModule_KeyType(srckey) != REDICTMODULE_KEYTYPE_LIST &&
         RedictModule_KeyType(srckey) != REDICTMODULE_KEYTYPE_EMPTY) ||
        (RedictModule_KeyType(dstkey) != REDICTMODULE_KEYTYPE_LIST &&
         RedictModule_KeyType(dstkey) != REDICTMODULE_KEYTYPE_EMPTY))
    {
        RedictModule_CloseKey(srckey);
        RedictModule_CloseKey(dstkey);
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    long long count;
    if ((RedictModule_StringToLongLong(argv[3],&count) != REDICTMODULE_OK) ||
        (count < 0)) {
        RedictModule_CloseKey(srckey);
        RedictModule_CloseKey(dstkey);
        return RedictModule_ReplyWithError(ctx,"ERR invalid count");
    }

    while(count-- > 0) {
        RedictModuleString *ele;

        ele = RedictModule_ListPop(srckey,REDICTMODULE_LIST_TAIL);
        if (ele == NULL) break;
        RedictModule_ListPush(dstkey,REDICTMODULE_LIST_HEAD,ele);
        RedictModule_FreeString(ctx,ele);
    }

    size_t len = RedictModule_ValueLength(srckey);
    RedictModule_CloseKey(srckey);
    RedictModule_CloseKey(dstkey);
    RedictModule_ReplyWithLongLong(ctx,len);
    return REDICTMODULE_OK;
}

/* Like the HELLO.LIST.SPLICE above, but uses automatic memory management
 * in order to avoid freeing stuff. */
int HelloListSpliceAuto_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 4) return RedictModule_WrongArity(ctx);

    RedictModule_AutoMemory(ctx);

    RedictModuleKey *srckey = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    RedictModuleKey *dstkey = RedictModule_OpenKey(ctx,argv[2],
        REDICTMODULE_READ|REDICTMODULE_WRITE);

    /* Src and dst key must be empty or lists. */
    if ((RedictModule_KeyType(srckey) != REDICTMODULE_KEYTYPE_LIST &&
         RedictModule_KeyType(srckey) != REDICTMODULE_KEYTYPE_EMPTY) ||
        (RedictModule_KeyType(dstkey) != REDICTMODULE_KEYTYPE_LIST &&
         RedictModule_KeyType(dstkey) != REDICTMODULE_KEYTYPE_EMPTY))
    {
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    long long count;
    if ((RedictModule_StringToLongLong(argv[3],&count) != REDICTMODULE_OK) ||
        (count < 0))
    {
        return RedictModule_ReplyWithError(ctx,"ERR invalid count");
    }

    while(count-- > 0) {
        RedictModuleString *ele;

        ele = RedictModule_ListPop(srckey,REDICTMODULE_LIST_TAIL);
        if (ele == NULL) break;
        RedictModule_ListPush(dstkey,REDICTMODULE_LIST_HEAD,ele);
    }

    size_t len = RedictModule_ValueLength(srckey);
    RedictModule_ReplyWithLongLong(ctx,len);
    return REDICTMODULE_OK;
}

/* HELLO.RAND.ARRAY <count>
 * Shows how to generate arrays as commands replies.
 * It just outputs <count> random numbers. */
int HelloRandArray_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);
    long long count;
    if (RedictModule_StringToLongLong(argv[1],&count) != REDICTMODULE_OK ||
        count < 0)
        return RedictModule_ReplyWithError(ctx,"ERR invalid count");

    /* To reply with an array, we call RedictModule_ReplyWithArray() followed
     * by other "count" calls to other reply functions in order to generate
     * the elements of the array. */
    RedictModule_ReplyWithArray(ctx,count);
    while(count--) RedictModule_ReplyWithLongLong(ctx,rand());
    return REDICTMODULE_OK;
}

/* This is a simple command to test replication. Because of the "!" modified
 * in the RedictModule_Call() call, the two INCRs get replicated.
 * Also note how the ECHO is replicated in an unexpected position (check
 * comments the function implementation). */
int HelloRepl1_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc)
{
    REDICTMODULE_NOT_USED(argv);
    REDICTMODULE_NOT_USED(argc);
    RedictModule_AutoMemory(ctx);

    /* This will be replicated *after* the two INCR statements, since
     * the Call() replication has precedence, so the actual replication
     * stream will be:
     *
     * MULTI
     * INCR foo
     * INCR bar
     * ECHO c foo
     * EXEC
     */
    RedictModule_Replicate(ctx,"ECHO","c","foo");

    /* Using the "!" modifier we replicate the command if it
     * modified the dataset in some way. */
    RedictModule_Call(ctx,"INCR","c!","foo");
    RedictModule_Call(ctx,"INCR","c!","bar");

    RedictModule_ReplyWithLongLong(ctx,0);

    return REDICTMODULE_OK;
}

/* Another command to show replication. In this case, we call
 * RedictModule_ReplicateVerbatim() to mean we want just the command to be
 * propagated to slaves / AOF exactly as it was called by the user.
 *
 * This command also shows how to work with string objects.
 * It takes a list, and increments all the elements (that must have
 * a numerical value) by 1, returning the sum of all the elements
 * as reply.
 *
 * Usage: HELLO.REPL2 <list-key> */
int HelloRepl2_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    RedictModule_AutoMemory(ctx); /* Use automatic memory management. */
    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);

    if (RedictModule_KeyType(key) != REDICTMODULE_KEYTYPE_LIST)
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);

    size_t listlen = RedictModule_ValueLength(key);
    long long sum = 0;

    /* Rotate and increment. */
    while(listlen--) {
        RedictModuleString *ele = RedictModule_ListPop(key,REDICTMODULE_LIST_TAIL);
        long long val;
        if (RedictModule_StringToLongLong(ele,&val) != REDICTMODULE_OK) val = 0;
        val++;
        sum += val;
        RedictModuleString *newele = RedictModule_CreateStringFromLongLong(ctx,val);
        RedictModule_ListPush(key,REDICTMODULE_LIST_HEAD,newele);
    }
    RedictModule_ReplyWithLongLong(ctx,sum);
    RedictModule_ReplicateVerbatim(ctx);
    return REDICTMODULE_OK;
}

/* This is an example of strings DMA access. Given a key containing a string
 * it toggles the case of each character from lower to upper case or the
 * other way around.
 *
 * No automatic memory management is used in this example (for the sake
 * of variety).
 *
 * HELLO.TOGGLE.CASE key */
int HelloToggleCase_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (argc != 2) return RedictModule_WrongArity(ctx);

    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);

    int keytype = RedictModule_KeyType(key);
    if (keytype != REDICTMODULE_KEYTYPE_STRING &&
        keytype != REDICTMODULE_KEYTYPE_EMPTY)
    {
        RedictModule_CloseKey(key);
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    if (keytype == REDICTMODULE_KEYTYPE_STRING) {
        size_t len, j;
        char *s = RedictModule_StringDMA(key,&len,REDICTMODULE_WRITE);
        for (j = 0; j < len; j++) {
            if (isupper(s[j])) {
                s[j] = tolower(s[j]);
            } else {
                s[j] = toupper(s[j]);
            }
        }
    }

    RedictModule_CloseKey(key);
    RedictModule_ReplyWithSimpleString(ctx,"OK");
    RedictModule_ReplicateVerbatim(ctx);
    return REDICTMODULE_OK;
}

/* HELLO.MORE.EXPIRE key milliseconds.
 *
 * If the key has already an associated TTL, extends it by "milliseconds"
 * milliseconds. Otherwise no operation is performed. */
int HelloMoreExpire_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    RedictModule_AutoMemory(ctx); /* Use automatic memory management. */
    if (argc != 3) return RedictModule_WrongArity(ctx);

    mstime_t addms, expire;

    if (RedictModule_StringToLongLong(argv[2],&addms) != REDICTMODULE_OK)
        return RedictModule_ReplyWithError(ctx,"ERR invalid expire time");

    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    expire = RedictModule_GetExpire(key);
    if (expire != REDICTMODULE_NO_EXPIRE) {
        expire += addms;
        RedictModule_SetExpire(key,expire);
    }
    return RedictModule_ReplyWithSimpleString(ctx,"OK");
}

/* HELLO.ZSUMRANGE key startscore endscore
 * Return the sum of all the scores elements between startscore and endscore.
 *
 * The computation is performed two times, one time from start to end and
 * another time backward. The two scores, returned as a two element array,
 * should match.*/
int HelloZsumRange_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    double score_start, score_end;
    if (argc != 4) return RedictModule_WrongArity(ctx);

    if (RedictModule_StringToDouble(argv[2],&score_start) != REDICTMODULE_OK ||
        RedictModule_StringToDouble(argv[3],&score_end) != REDICTMODULE_OK)
    {
        return RedictModule_ReplyWithError(ctx,"ERR invalid range");
    }

    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    if (RedictModule_KeyType(key) != REDICTMODULE_KEYTYPE_ZSET) {
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    double scoresum_a = 0;
    double scoresum_b = 0;

    RedictModule_ZsetFirstInScoreRange(key,score_start,score_end,0,0);
    while(!RedictModule_ZsetRangeEndReached(key)) {
        double score;
        RedictModuleString *ele = RedictModule_ZsetRangeCurrentElement(key,&score);
        RedictModule_FreeString(ctx,ele);
        scoresum_a += score;
        RedictModule_ZsetRangeNext(key);
    }
    RedictModule_ZsetRangeStop(key);

    RedictModule_ZsetLastInScoreRange(key,score_start,score_end,0,0);
    while(!RedictModule_ZsetRangeEndReached(key)) {
        double score;
        RedictModuleString *ele = RedictModule_ZsetRangeCurrentElement(key,&score);
        RedictModule_FreeString(ctx,ele);
        scoresum_b += score;
        RedictModule_ZsetRangePrev(key);
    }

    RedictModule_ZsetRangeStop(key);

    RedictModule_CloseKey(key);

    RedictModule_ReplyWithArray(ctx,2);
    RedictModule_ReplyWithDouble(ctx,scoresum_a);
    RedictModule_ReplyWithDouble(ctx,scoresum_b);
    return REDICTMODULE_OK;
}

/* HELLO.LEXRANGE key min_lex max_lex min_age max_age
 * This command expects a sorted set stored at key in the following form:
 * - All the elements have score 0.
 * - Elements are pairs of "<name>:<age>", for example "Anna:52".
 * The command will return all the sorted set items that are lexicographically
 * between the specified range (using the same format as ZRANGEBYLEX)
 * and having an age between min_age and max_age. */
int HelloLexRange_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    RedictModule_AutoMemory(ctx); /* Use automatic memory management. */

    if (argc != 6) return RedictModule_WrongArity(ctx);

    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    if (RedictModule_KeyType(key) != REDICTMODULE_KEYTYPE_ZSET) {
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    if (RedictModule_ZsetFirstInLexRange(key,argv[2],argv[3]) != REDICTMODULE_OK) {
        return RedictModule_ReplyWithError(ctx,"invalid range");
    }

    int arraylen = 0;
    RedictModule_ReplyWithArray(ctx,REDICTMODULE_POSTPONED_LEN);
    while(!RedictModule_ZsetRangeEndReached(key)) {
        double score;
        RedictModuleString *ele = RedictModule_ZsetRangeCurrentElement(key,&score);
        RedictModule_ReplyWithString(ctx,ele);
        RedictModule_FreeString(ctx,ele);
        RedictModule_ZsetRangeNext(key);
        arraylen++;
    }
    RedictModule_ZsetRangeStop(key);
    RedictModule_ReplySetArrayLength(ctx,arraylen);
    RedictModule_CloseKey(key);
    return REDICTMODULE_OK;
}

/* HELLO.HCOPY key srcfield dstfield
 * This is just an example command that sets the hash field dstfield to the
 * same value of srcfield. If srcfield does not exist no operation is
 * performed.
 *
 * The command returns 1 if the copy is performed (srcfield exists) otherwise
 * 0 is returned. */
int HelloHCopy_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    RedictModule_AutoMemory(ctx); /* Use automatic memory management. */

    if (argc != 4) return RedictModule_WrongArity(ctx);
    RedictModuleKey *key = RedictModule_OpenKey(ctx,argv[1],
        REDICTMODULE_READ|REDICTMODULE_WRITE);
    int type = RedictModule_KeyType(key);
    if (type != REDICTMODULE_KEYTYPE_HASH &&
        type != REDICTMODULE_KEYTYPE_EMPTY)
    {
        return RedictModule_ReplyWithError(ctx,REDICTMODULE_ERRORMSG_WRONGTYPE);
    }

    /* Get the old field value. */
    RedictModuleString *oldval;
    RedictModule_HashGet(key,REDICTMODULE_HASH_NONE,argv[2],&oldval,NULL);
    if (oldval) {
        RedictModule_HashSet(key,REDICTMODULE_HASH_NONE,argv[3],oldval,NULL);
    }
    RedictModule_ReplyWithLongLong(ctx,oldval != NULL);
    return REDICTMODULE_OK;
}

/* HELLO.LEFTPAD str len ch
 * This is an implementation of the infamous LEFTPAD function, that
 * was at the center of an issue with the npm modules system in March 2016.
 *
 * LEFTPAD is a good example of using a Redict Modules API called
 * "pool allocator", that was a famous way to allocate memory in yet another
 * open source project, the Apache web server.
 *
 * The concept is very simple: there is memory that is useful to allocate
 * only in the context of serving a request, and must be freed anyway when
 * the callback implementing the command returns. So in that case the module
 * does not need to retain a reference to these allocations, it is just
 * required to free the memory before returning. When this is the case the
 * module can call RedictModule_PoolAlloc() instead, that works like malloc()
 * but will automatically free the memory when the module callback returns.
 *
 * Note that PoolAlloc() does not necessarily require AutoMemory to be
 * active. */
int HelloLeftPad_RedictCommand(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    RedictModule_AutoMemory(ctx); /* Use automatic memory management. */
    long long padlen;

    if (argc != 4) return RedictModule_WrongArity(ctx);

    if ((RedictModule_StringToLongLong(argv[2],&padlen) != REDICTMODULE_OK) ||
        (padlen< 0)) {
        return RedictModule_ReplyWithError(ctx,"ERR invalid padding length");
    }
    size_t strlen, chlen;
    const char *str = RedictModule_StringPtrLen(argv[1], &strlen);
    const char *ch = RedictModule_StringPtrLen(argv[3], &chlen);

    /* If the string is already larger than the target len, just return
     * the string itself. */
    if (strlen >= (size_t)padlen)
        return RedictModule_ReplyWithString(ctx,argv[1]);

    /* Padding must be a single character in this simple implementation. */
    if (chlen != 1)
        return RedictModule_ReplyWithError(ctx,
            "ERR padding must be a single char");

    /* Here we use our pool allocator, for our throw-away allocation. */
    padlen -= strlen;
    char *buf = RedictModule_PoolAlloc(ctx,padlen+strlen);
    for (long long j = 0; j < padlen; j++) buf[j] = *ch;
    memcpy(buf+padlen,str,strlen);

    RedictModule_ReplyWithStringBuffer(ctx,buf,padlen+strlen);
    return REDICTMODULE_OK;
}

/* This function must be present on each Redict module. It is used in order to
 * register the commands into the Redict server. */
int RedictModule_OnLoad(RedictModuleCtx *ctx, RedictModuleString **argv, int argc) {
    if (RedictModule_Init(ctx,"helloworld",1,REDICTMODULE_APIVER_1)
        == REDICTMODULE_ERR) return REDICTMODULE_ERR;

    /* Log the list of parameters passing loading the module. */
    for (int j = 0; j < argc; j++) {
        const char *s = RedictModule_StringPtrLen(argv[j],NULL);
        printf("Module loaded with ARGV[%d] = %s\n", j, s);
    }

    if (RedictModule_CreateCommand(ctx,"hello.simple",
        HelloSimple_RedictCommand,"readonly",0,0,0) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.push.native",
        HelloPushNative_RedictCommand,"write deny-oom",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.push.call",
        HelloPushCall_RedictCommand,"write deny-oom",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.push.call2",
        HelloPushCall2_RedictCommand,"write deny-oom",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.list.sum.len",
        HelloListSumLen_RedictCommand,"readonly",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.list.splice",
        HelloListSplice_RedictCommand,"write deny-oom",1,2,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.list.splice.auto",
        HelloListSpliceAuto_RedictCommand,
        "write deny-oom",1,2,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.rand.array",
        HelloRandArray_RedictCommand,"readonly",0,0,0) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.repl1",
        HelloRepl1_RedictCommand,"write",0,0,0) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.repl2",
        HelloRepl2_RedictCommand,"write",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.toggle.case",
        HelloToggleCase_RedictCommand,"write",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.more.expire",
        HelloMoreExpire_RedictCommand,"write",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.zsumrange",
        HelloZsumRange_RedictCommand,"readonly",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.lexrange",
        HelloLexRange_RedictCommand,"readonly",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.hcopy",
        HelloHCopy_RedictCommand,"write deny-oom",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    if (RedictModule_CreateCommand(ctx,"hello.leftpad",
        HelloLeftPad_RedictCommand,"",1,1,1) == REDICTMODULE_ERR)
        return REDICTMODULE_ERR;

    return REDICTMODULE_OK;
}
