mirror of https://github.com/redis/redis.git
				
				
				
			
		
			
				
	
	
		
			624 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
#!/usr/bin/env python3
 | 
						|
import glob
 | 
						|
import json
 | 
						|
import os
 | 
						|
import argparse
 | 
						|
 | 
						|
ARG_TYPES = {
 | 
						|
    "string": "ARG_TYPE_STRING",
 | 
						|
    "integer": "ARG_TYPE_INTEGER",
 | 
						|
    "double": "ARG_TYPE_DOUBLE",
 | 
						|
    "key": "ARG_TYPE_KEY",
 | 
						|
    "pattern": "ARG_TYPE_PATTERN",
 | 
						|
    "unix-time": "ARG_TYPE_UNIX_TIME",
 | 
						|
    "pure-token": "ARG_TYPE_PURE_TOKEN",
 | 
						|
    "oneof": "ARG_TYPE_ONEOF",
 | 
						|
    "block": "ARG_TYPE_BLOCK",
 | 
						|
}
 | 
						|
 | 
						|
GROUPS = {
 | 
						|
    "generic": "COMMAND_GROUP_GENERIC",
 | 
						|
    "string": "COMMAND_GROUP_STRING",
 | 
						|
    "list": "COMMAND_GROUP_LIST",
 | 
						|
    "set": "COMMAND_GROUP_SET",
 | 
						|
    "sorted_set": "COMMAND_GROUP_SORTED_SET",
 | 
						|
    "hash": "COMMAND_GROUP_HASH",
 | 
						|
    "pubsub": "COMMAND_GROUP_PUBSUB",
 | 
						|
    "transactions": "COMMAND_GROUP_TRANSACTIONS",
 | 
						|
    "connection": "COMMAND_GROUP_CONNECTION",
 | 
						|
    "server": "COMMAND_GROUP_SERVER",
 | 
						|
    "scripting": "COMMAND_GROUP_SCRIPTING",
 | 
						|
    "hyperloglog": "COMMAND_GROUP_HYPERLOGLOG",
 | 
						|
    "cluster": "COMMAND_GROUP_CLUSTER",
 | 
						|
    "sentinel": "COMMAND_GROUP_SENTINEL",
 | 
						|
    "geo": "COMMAND_GROUP_GEO",
 | 
						|
    "stream": "COMMAND_GROUP_STREAM",
 | 
						|
    "bitmap": "COMMAND_GROUP_BITMAP",
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
def get_optional_desc_string(desc, field, force_uppercase=False):
 | 
						|
    v = desc.get(field, None)
 | 
						|
    if v and force_uppercase:
 | 
						|
        v = v.upper()
 | 
						|
    ret = "\"%s\"" % v if v else "NULL"
 | 
						|
    return ret.replace("\n", "\\n")
 | 
						|
 | 
						|
 | 
						|
def check_command_args_key_specs(args, command_key_specs_index_set, command_arg_key_specs_index_set):
 | 
						|
    if not args:
 | 
						|
        return True
 | 
						|
 | 
						|
    for arg in args:
 | 
						|
        if arg.key_spec_index is not None:
 | 
						|
            assert isinstance(arg.key_spec_index, int)
 | 
						|
 | 
						|
            if arg.key_spec_index not in command_key_specs_index_set:
 | 
						|
                print("command: %s arg: %s key_spec_index error" % (command.fullname(), arg.name))
 | 
						|
                return False
 | 
						|
 | 
						|
            command_arg_key_specs_index_set.add(arg.key_spec_index)
 | 
						|
 | 
						|
        if not check_command_args_key_specs(arg.subargs, command_key_specs_index_set, command_arg_key_specs_index_set):
 | 
						|
            return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
def check_command_key_specs(command):
 | 
						|
    if not command.key_specs:
 | 
						|
        return True
 | 
						|
 | 
						|
    assert isinstance(command.key_specs, list)
 | 
						|
 | 
						|
    for cmd_key_spec in command.key_specs:
 | 
						|
        if "flags" not in cmd_key_spec:
 | 
						|
            print("command: %s key_specs missing flags" % command.fullname())
 | 
						|
            return False
 | 
						|
 | 
						|
        if "NOT_KEY" in cmd_key_spec["flags"]:
 | 
						|
            # Like SUNSUBSCRIBE / SPUBLISH / SSUBSCRIBE
 | 
						|
            return True
 | 
						|
 | 
						|
    command_key_specs_index_set = set(range(len(command.key_specs)))
 | 
						|
    command_arg_key_specs_index_set = set()
 | 
						|
 | 
						|
    # Collect key_spec used for each arg, including arg.subarg
 | 
						|
    if not check_command_args_key_specs(command.args, command_key_specs_index_set, command_arg_key_specs_index_set):
 | 
						|
        return False
 | 
						|
 | 
						|
    # Check if we have key_specs not used
 | 
						|
    if command_key_specs_index_set != command_arg_key_specs_index_set:
 | 
						|
        print("command: %s may have unused key_spec" % command.fullname())
 | 
						|
        return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
# Globals
 | 
						|
subcommands = {}  # container_name -> dict(subcommand_name -> Subcommand) - Only subcommands
 | 
						|
commands = {}  # command_name -> Command - Only commands
 | 
						|
 | 
						|
 | 
						|
class KeySpec(object):
 | 
						|
    def __init__(self, spec):
 | 
						|
        self.spec = spec
 | 
						|
 | 
						|
    def struct_code(self):
 | 
						|
        def _flags_code():
 | 
						|
            s = ""
 | 
						|
            for flag in self.spec.get("flags", []):
 | 
						|
                s += "CMD_KEY_%s|" % flag
 | 
						|
            return s[:-1] if s else 0
 | 
						|
 | 
						|
        def _begin_search_code():
 | 
						|
            if self.spec["begin_search"].get("index"):
 | 
						|
                return "KSPEC_BS_INDEX,.bs.index={%d}" % (
 | 
						|
                    self.spec["begin_search"]["index"]["pos"]
 | 
						|
                )
 | 
						|
            elif self.spec["begin_search"].get("keyword"):
 | 
						|
                return "KSPEC_BS_KEYWORD,.bs.keyword={\"%s\",%d}" % (
 | 
						|
                    self.spec["begin_search"]["keyword"]["keyword"],
 | 
						|
                    self.spec["begin_search"]["keyword"]["startfrom"],
 | 
						|
                )
 | 
						|
            elif "unknown" in self.spec["begin_search"]:
 | 
						|
                return "KSPEC_BS_UNKNOWN,{{0}}"
 | 
						|
            else:
 | 
						|
                print("Invalid begin_search! value=%s" % self.spec["begin_search"])
 | 
						|
                exit(1)
 | 
						|
 | 
						|
        def _find_keys_code():
 | 
						|
            if self.spec["find_keys"].get("range"):
 | 
						|
                return "KSPEC_FK_RANGE,.fk.range={%d,%d,%d}" % (
 | 
						|
                    self.spec["find_keys"]["range"]["lastkey"],
 | 
						|
                    self.spec["find_keys"]["range"]["step"],
 | 
						|
                    self.spec["find_keys"]["range"]["limit"]
 | 
						|
                )
 | 
						|
            elif self.spec["find_keys"].get("keynum"):
 | 
						|
                return "KSPEC_FK_KEYNUM,.fk.keynum={%d,%d,%d}" % (
 | 
						|
                    self.spec["find_keys"]["keynum"]["keynumidx"],
 | 
						|
                    self.spec["find_keys"]["keynum"]["firstkey"],
 | 
						|
                    self.spec["find_keys"]["keynum"]["step"]
 | 
						|
                )
 | 
						|
            elif "unknown" in self.spec["find_keys"]:
 | 
						|
                return "KSPEC_FK_UNKNOWN,{{0}}"
 | 
						|
            else:
 | 
						|
                print("Invalid find_keys! value=%s" % self.spec["find_keys"])
 | 
						|
                exit(1)
 | 
						|
 | 
						|
        return "%s,%s,%s,%s" % (
 | 
						|
            get_optional_desc_string(self.spec, "notes"),
 | 
						|
            _flags_code(),
 | 
						|
            _begin_search_code(),
 | 
						|
            _find_keys_code()
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def verify_no_dup_names(container_fullname, args):
 | 
						|
    name_list = [arg.name for arg in args]
 | 
						|
    name_set = set(name_list)
 | 
						|
    if len(name_list) != len(name_set):
 | 
						|
        print("{}: Dup argument names: {}".format(container_fullname, name_list))
 | 
						|
        exit(1)
 | 
						|
 | 
						|
 | 
						|
class Argument(object):
 | 
						|
    def __init__(self, parent_name, desc):
 | 
						|
        self.parent_name = parent_name
 | 
						|
        self.desc = desc
 | 
						|
        self.name = self.desc["name"].lower()
 | 
						|
        if "_" in self.name:
 | 
						|
            print("{}: name ({}) should not contain underscores".format(self.fullname(), self.name))
 | 
						|
            exit(1)
 | 
						|
        self.type = self.desc["type"]
 | 
						|
        self.key_spec_index = self.desc.get("key_spec_index", None)
 | 
						|
        self.subargs = []
 | 
						|
        if self.type in ["oneof", "block"]:
 | 
						|
            self.display = None
 | 
						|
            for subdesc in self.desc["arguments"]:
 | 
						|
                self.subargs.append(Argument(self.fullname(), subdesc))
 | 
						|
            if len(self.subargs) < 2:
 | 
						|
                print("{}: oneof or block arg contains less than two subargs".format(self.fullname()))
 | 
						|
                exit(1)
 | 
						|
            verify_no_dup_names(self.fullname(), self.subargs)
 | 
						|
        else:
 | 
						|
            self.display = self.desc.get("display")
 | 
						|
 | 
						|
    def fullname(self):
 | 
						|
        return ("%s %s" % (self.parent_name, self.name)).replace("-", "_")
 | 
						|
 | 
						|
    def struct_name(self):
 | 
						|
        return "%s_Arg" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def subarg_table_name(self):
 | 
						|
        assert self.subargs
 | 
						|
        return "%s_Subargs" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def struct_code(self):
 | 
						|
        """
 | 
						|
        Output example:
 | 
						|
        MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs
 | 
						|
        """
 | 
						|
 | 
						|
        def _flags_code():
 | 
						|
            s = ""
 | 
						|
            if self.desc.get("optional", False):
 | 
						|
                s += "CMD_ARG_OPTIONAL|"
 | 
						|
            if self.desc.get("multiple", False):
 | 
						|
                s += "CMD_ARG_MULTIPLE|"
 | 
						|
            if self.desc.get("multiple_token", False):
 | 
						|
                assert self.desc.get("multiple", False)  # Sanity
 | 
						|
                s += "CMD_ARG_MULTIPLE_TOKEN|"
 | 
						|
            return s[:-1] if s else "CMD_ARG_NONE"
 | 
						|
 | 
						|
        s = "MAKE_ARG(\"%s\",%s,%d,%s,%s,%s,%s,%d,%s)" % (
 | 
						|
            self.name,
 | 
						|
            ARG_TYPES[self.type],
 | 
						|
            self.desc.get("key_spec_index", -1),
 | 
						|
            get_optional_desc_string(self.desc, "token", force_uppercase=True),
 | 
						|
            get_optional_desc_string(self.desc, "summary"),
 | 
						|
            get_optional_desc_string(self.desc, "since"),
 | 
						|
            _flags_code(),
 | 
						|
            len(self.subargs),
 | 
						|
            get_optional_desc_string(self.desc, "deprecated_since"),
 | 
						|
        )
 | 
						|
        if "display" in self.desc:
 | 
						|
            s += ",.display_text=\"%s\"" % self.desc["display"].lower()
 | 
						|
        if self.subargs:
 | 
						|
            s += ",.subargs=%s" % self.subarg_table_name()
 | 
						|
 | 
						|
        return s
 | 
						|
 | 
						|
    def write_internal_structs(self, f):
 | 
						|
        if self.subargs:
 | 
						|
            for subarg in self.subargs:
 | 
						|
                subarg.write_internal_structs(f)
 | 
						|
 | 
						|
            f.write("/* %s argument table */\n" % self.fullname())
 | 
						|
            f.write("struct COMMAND_ARG %s[] = {\n" % self.subarg_table_name())
 | 
						|
            for subarg in self.subargs:
 | 
						|
                f.write("{%s},\n" % subarg.struct_code())
 | 
						|
            f.write("};\n\n")
 | 
						|
 | 
						|
 | 
						|
def to_c_name(str):
 | 
						|
    return str.replace(":", "").replace(".", "_").replace("$", "_")\
 | 
						|
        .replace("^", "_").replace("*", "_").replace("-", "_")
 | 
						|
 | 
						|
 | 
						|
class ReplySchema(object):
 | 
						|
    def __init__(self, name, desc):
 | 
						|
        self.name = to_c_name(name)
 | 
						|
        self.schema = {}
 | 
						|
        if desc.get("type") == "object":
 | 
						|
            if desc.get("properties") and desc.get("additionalProperties") is None:
 | 
						|
                print("%s: Any object that has properties should have the additionalProperties field" % self.name)
 | 
						|
                exit(1)
 | 
						|
        elif desc.get("type") == "array":
 | 
						|
            if desc.get("items") and isinstance(desc["items"], list) and any([desc.get(k) is None for k in ["minItems", "maxItems"]]):
 | 
						|
                print("%s: Any array that has items should have the minItems and maxItems fields" % self.name)
 | 
						|
                exit(1)
 | 
						|
        for k, v in desc.items():
 | 
						|
            if isinstance(v, dict):
 | 
						|
                self.schema[k] = ReplySchema("%s_%s" % (self.name, k), v)
 | 
						|
            elif isinstance(v, list):
 | 
						|
                self.schema[k] = []
 | 
						|
                for i, subdesc in enumerate(v):
 | 
						|
                    self.schema[k].append(ReplySchema("%s_%s_%i" % (self.name, k,i), subdesc))
 | 
						|
            else:
 | 
						|
                self.schema[k] = v
 | 
						|
    
 | 
						|
    def write(self, f):
 | 
						|
        def struct_code(name, k, v):
 | 
						|
            if isinstance(v, ReplySchema):
 | 
						|
                t = "JSON_TYPE_OBJECT"
 | 
						|
                vstr = ".value.object=&%s" % name
 | 
						|
            elif isinstance(v, list):
 | 
						|
                t = "JSON_TYPE_ARRAY"
 | 
						|
                vstr = ".value.array={.objects=%s,.length=%d}" % (name, len(v))
 | 
						|
            elif isinstance(v, bool):
 | 
						|
                t = "JSON_TYPE_BOOLEAN"
 | 
						|
                vstr = ".value.boolean=%d" % int(v)
 | 
						|
            elif isinstance(v, str):
 | 
						|
                t = "JSON_TYPE_STRING"
 | 
						|
                vstr = ".value.string=\"%s\"" % v
 | 
						|
            elif isinstance(v, int):
 | 
						|
                t = "JSON_TYPE_INTEGER"
 | 
						|
                vstr = ".value.integer=%d" % v
 | 
						|
            
 | 
						|
            return "%s,\"%s\",%s" % (t, k, vstr)
 | 
						|
 | 
						|
        for k, v in self.schema.items():
 | 
						|
            if isinstance(v, ReplySchema):
 | 
						|
                v.write(f)
 | 
						|
            elif isinstance(v, list):
 | 
						|
                for i, schema in enumerate(v):
 | 
						|
                    schema.write(f)
 | 
						|
                name = to_c_name("%s_%s" % (self.name, k))
 | 
						|
                f.write("/* %s array reply schema */\n" % name)
 | 
						|
                f.write("struct jsonObject *%s[] = {\n" % name)
 | 
						|
                for i, schema in enumerate(v):
 | 
						|
                    f.write("&%s,\n" % schema.name)
 | 
						|
                f.write("};\n\n")
 | 
						|
            
 | 
						|
        f.write("/* %s reply schema */\n" % self.name)
 | 
						|
        f.write("struct jsonObjectElement %s_elements[] = {\n" % self.name)
 | 
						|
        for k, v in self.schema.items():
 | 
						|
            name = to_c_name("%s_%s" % (self.name, k))
 | 
						|
            f.write("{%s},\n" % struct_code(name, k, v))
 | 
						|
        f.write("};\n\n")
 | 
						|
        f.write("struct jsonObject %s = {%s_elements,.length=%d};\n\n" % (self.name, self.name, len(self.schema)))
 | 
						|
 | 
						|
 | 
						|
class Command(object):
 | 
						|
    def __init__(self, name, desc):
 | 
						|
        self.name = name.upper()
 | 
						|
        self.desc = desc
 | 
						|
        self.group = self.desc["group"]
 | 
						|
        self.key_specs = self.desc.get("key_specs", [])
 | 
						|
        self.subcommands = []
 | 
						|
        self.args = []
 | 
						|
        for arg_desc in self.desc.get("arguments", []):
 | 
						|
            self.args.append(Argument(self.fullname(), arg_desc))
 | 
						|
        verify_no_dup_names(self.fullname(), self.args)
 | 
						|
        self.reply_schema = None
 | 
						|
        if "reply_schema" in self.desc:
 | 
						|
            self.reply_schema = ReplySchema(self.reply_schema_name(), self.desc["reply_schema"])
 | 
						|
 | 
						|
    def fullname(self):
 | 
						|
        return self.name.replace("-", "_").replace(":", "")
 | 
						|
 | 
						|
    def return_types_table_name(self):
 | 
						|
        return "%s_ReturnInfo" % self.fullname().replace(" ", "_")
 | 
						|
 | 
						|
    def subcommand_table_name(self):
 | 
						|
        assert self.subcommands
 | 
						|
        return "%s_Subcommands" % self.name
 | 
						|
 | 
						|
    def history_table_name(self):
 | 
						|
        return "%s_History" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def tips_table_name(self):
 | 
						|
        return "%s_Tips" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def arg_table_name(self):
 | 
						|
        return "%s_Args" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def key_specs_table_name(self):
 | 
						|
        return "%s_Keyspecs" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def reply_schema_name(self):
 | 
						|
        return "%s_ReplySchema" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def struct_name(self):
 | 
						|
        return "%s_Command" % (self.fullname().replace(" ", "_"))
 | 
						|
 | 
						|
    def history_code(self):
 | 
						|
        if not self.desc.get("history"):
 | 
						|
            return ""
 | 
						|
        s = ""
 | 
						|
        for tupl in self.desc["history"]:
 | 
						|
            s += "{\"%s\",\"%s\"},\n" % (tupl[0], tupl[1])
 | 
						|
        return s
 | 
						|
 | 
						|
    def num_history(self):
 | 
						|
        if not self.desc.get("history"):
 | 
						|
            return 0
 | 
						|
        return len(self.desc["history"])
 | 
						|
 | 
						|
    def tips_code(self):
 | 
						|
        if not self.desc.get("command_tips"):
 | 
						|
            return ""
 | 
						|
        s = ""
 | 
						|
        for hint in self.desc["command_tips"]:
 | 
						|
            s += "\"%s\",\n" % hint.lower()
 | 
						|
        return s
 | 
						|
 | 
						|
    def num_tips(self):
 | 
						|
        if not self.desc.get("command_tips"):
 | 
						|
            return 0
 | 
						|
        return len(self.desc["command_tips"])
 | 
						|
 | 
						|
    def key_specs_code(self):
 | 
						|
        s = ""
 | 
						|
        for spec in self.key_specs:
 | 
						|
            s += "{%s}," % KeySpec(spec).struct_code()
 | 
						|
        return s[:-1]
 | 
						|
 | 
						|
 | 
						|
    def struct_code(self):
 | 
						|
        """
 | 
						|
        Output example:
 | 
						|
        MAKE_CMD("set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args
 | 
						|
        """
 | 
						|
 | 
						|
        def _flags_code():
 | 
						|
            s = ""
 | 
						|
            for flag in self.desc.get("command_flags", []):
 | 
						|
                s += "CMD_%s|" % flag
 | 
						|
            return s[:-1] if s else 0
 | 
						|
 | 
						|
        def _acl_categories_code():
 | 
						|
            s = ""
 | 
						|
            for cat in self.desc.get("acl_categories", []):
 | 
						|
                s += "ACL_CATEGORY_%s|" % cat
 | 
						|
            return s[:-1] if s else 0
 | 
						|
 | 
						|
        def _doc_flags_code():
 | 
						|
            s = ""
 | 
						|
            for flag in self.desc.get("doc_flags", []):
 | 
						|
                s += "CMD_DOC_%s|" % flag
 | 
						|
            return s[:-1] if s else "CMD_DOC_NONE"
 | 
						|
 | 
						|
        s = "MAKE_CMD(\"%s\",%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%s,%d,%s,%d,%s,%s,%s,%d,%s,%d)," % (
 | 
						|
            self.name.lower(),
 | 
						|
            get_optional_desc_string(self.desc, "summary"),
 | 
						|
            get_optional_desc_string(self.desc, "complexity"),
 | 
						|
            get_optional_desc_string(self.desc, "since"),
 | 
						|
            _doc_flags_code(),
 | 
						|
            get_optional_desc_string(self.desc, "replaced_by"),
 | 
						|
            get_optional_desc_string(self.desc, "deprecated_since"),
 | 
						|
            "\"%s\"" % self.group,
 | 
						|
            GROUPS[self.group],
 | 
						|
            self.history_table_name(),
 | 
						|
            self.num_history(),
 | 
						|
            self.tips_table_name(),
 | 
						|
            self.num_tips(),
 | 
						|
            self.desc.get("function", "NULL"),
 | 
						|
            self.desc["arity"],
 | 
						|
            _flags_code(),
 | 
						|
            _acl_categories_code(),
 | 
						|
            self.key_specs_table_name(),
 | 
						|
            len(self.key_specs),
 | 
						|
            self.desc.get("get_keys_function", "NULL"),
 | 
						|
            len(self.args),
 | 
						|
        )
 | 
						|
 | 
						|
        if self.subcommands:
 | 
						|
            s += ".subcommands=%s," % self.subcommand_table_name()
 | 
						|
 | 
						|
        if self.args:
 | 
						|
            s += ".args=%s," % self.arg_table_name()
 | 
						|
 | 
						|
        if self.reply_schema and args.with_reply_schema:
 | 
						|
            s += ".reply_schema=&%s," % self.reply_schema_name()
 | 
						|
 | 
						|
        return s[:-1]
 | 
						|
 | 
						|
    def write_internal_structs(self, f):
 | 
						|
        if self.subcommands:
 | 
						|
            subcommand_list = sorted(self.subcommands, key=lambda cmd: cmd.name)
 | 
						|
            for subcommand in subcommand_list:
 | 
						|
                subcommand.write_internal_structs(f)
 | 
						|
 | 
						|
            f.write("/* %s command table */\n" % self.fullname())
 | 
						|
            f.write("struct COMMAND_STRUCT %s[] = {\n" % self.subcommand_table_name())
 | 
						|
            for subcommand in subcommand_list:
 | 
						|
                f.write("{%s},\n" % subcommand.struct_code())
 | 
						|
            f.write("{0}\n")
 | 
						|
            f.write("};\n\n")
 | 
						|
 | 
						|
        f.write("/********** %s ********************/\n\n" % self.fullname())
 | 
						|
 | 
						|
        f.write("#ifndef SKIP_CMD_HISTORY_TABLE\n")
 | 
						|
        f.write("/* %s history */\n" % self.fullname())
 | 
						|
        code = self.history_code()
 | 
						|
        if code:
 | 
						|
            f.write("commandHistory %s[] = {\n" % self.history_table_name())
 | 
						|
            f.write("%s" % code)
 | 
						|
            f.write("};\n")
 | 
						|
        else:
 | 
						|
            f.write("#define %s NULL\n" % self.history_table_name())
 | 
						|
        f.write("#endif\n\n")
 | 
						|
 | 
						|
        f.write("#ifndef SKIP_CMD_TIPS_TABLE\n")
 | 
						|
        f.write("/* %s tips */\n" % self.fullname())
 | 
						|
        code = self.tips_code()
 | 
						|
        if code:
 | 
						|
            f.write("const char *%s[] = {\n" % self.tips_table_name())
 | 
						|
            f.write("%s" % code)
 | 
						|
            f.write("};\n")
 | 
						|
        else:
 | 
						|
            f.write("#define %s NULL\n" % self.tips_table_name())
 | 
						|
        f.write("#endif\n\n")
 | 
						|
 | 
						|
        f.write("#ifndef SKIP_CMD_KEY_SPECS_TABLE\n")
 | 
						|
        f.write("/* %s key specs */\n" % self.fullname())
 | 
						|
        code = self.key_specs_code()
 | 
						|
        if code:
 | 
						|
            f.write("keySpec %s[%d] = {\n" % (self.key_specs_table_name(), len(self.key_specs)))
 | 
						|
            f.write("%s\n" % code)
 | 
						|
            f.write("};\n")
 | 
						|
        else:
 | 
						|
            f.write("#define %s NULL\n" % self.key_specs_table_name())
 | 
						|
        f.write("#endif\n\n")
 | 
						|
 | 
						|
        if self.args:
 | 
						|
            for arg in self.args:
 | 
						|
                arg.write_internal_structs(f)
 | 
						|
 | 
						|
            f.write("/* %s argument table */\n" % self.fullname())
 | 
						|
            f.write("struct COMMAND_ARG %s[] = {\n" % self.arg_table_name())
 | 
						|
            for arg in self.args:
 | 
						|
                f.write("{%s},\n" % arg.struct_code())
 | 
						|
            f.write("};\n\n")
 | 
						|
 | 
						|
        if self.reply_schema and args.with_reply_schema:
 | 
						|
            self.reply_schema.write(f)
 | 
						|
 | 
						|
 | 
						|
class Subcommand(Command):
 | 
						|
    def __init__(self, name, desc):
 | 
						|
        self.container_name = desc["container"].upper()
 | 
						|
        super(Subcommand, self).__init__(name, desc)
 | 
						|
 | 
						|
    def fullname(self):
 | 
						|
        return "%s %s" % (self.container_name, self.name.replace("-", "_").replace(":", ""))
 | 
						|
 | 
						|
 | 
						|
def create_command(name, desc):
 | 
						|
    if desc.get("container"):
 | 
						|
        cmd = Subcommand(name.upper(), desc)
 | 
						|
        subcommands.setdefault(desc["container"].upper(), {})[name] = cmd
 | 
						|
    else:
 | 
						|
        cmd = Command(name.upper(), desc)
 | 
						|
        commands[name.upper()] = cmd
 | 
						|
 | 
						|
 | 
						|
# MAIN
 | 
						|
 | 
						|
# Figure out where the sources are
 | 
						|
srcdir = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../src")
 | 
						|
 | 
						|
parser = argparse.ArgumentParser()
 | 
						|
parser.add_argument('--with-reply-schema', action='store_true')
 | 
						|
args = parser.parse_args()
 | 
						|
 | 
						|
# Create all command objects
 | 
						|
print("Processing json files...")
 | 
						|
for filename in glob.glob('%s/commands/*.json' % srcdir):
 | 
						|
    with open(filename, "r") as f:
 | 
						|
        try:
 | 
						|
            d = json.load(f)
 | 
						|
            for name, desc in d.items():
 | 
						|
                create_command(name, desc)
 | 
						|
        except json.decoder.JSONDecodeError as err:
 | 
						|
            print("Error processing %s: %s" % (filename, err))
 | 
						|
            exit(1)
 | 
						|
 | 
						|
# Link subcommands to containers
 | 
						|
print("Linking container command to subcommands...")
 | 
						|
for command in commands.values():
 | 
						|
    assert command.group
 | 
						|
    if command.name not in subcommands:
 | 
						|
        continue
 | 
						|
    for subcommand in subcommands[command.name].values():
 | 
						|
        assert not subcommand.group or subcommand.group == command.group
 | 
						|
        subcommand.group = command.group
 | 
						|
        command.subcommands.append(subcommand)
 | 
						|
 | 
						|
check_command_error_counter = 0  # An error counter is used to count errors in command checking.
 | 
						|
 | 
						|
print("Checking all commands...")
 | 
						|
for command in commands.values():
 | 
						|
    if not check_command_key_specs(command):
 | 
						|
        check_command_error_counter += 1
 | 
						|
 | 
						|
if check_command_error_counter != 0:
 | 
						|
    print("Error: There are errors in the commands check, please check the above logs.")
 | 
						|
    exit(1)
 | 
						|
 | 
						|
commands_filename = "commands_with_reply_schema" if args.with_reply_schema else "commands"
 | 
						|
print("Generating %s.def..." % commands_filename)
 | 
						|
with open("%s/%s.def" % (srcdir, commands_filename), "w") as f:
 | 
						|
    f.write("/* Automatically generated by %s, do not edit. */\n\n" % os.path.basename(__file__))
 | 
						|
    f.write(
 | 
						|
"""
 | 
						|
/* We have fabulous commands from
 | 
						|
 * the fantastic
 | 
						|
 * Redis Command Table! */
 | 
						|
 | 
						|
/* Must match redisCommandGroup */
 | 
						|
const char *COMMAND_GROUP_STR[] = {
 | 
						|
    "generic",
 | 
						|
    "string",
 | 
						|
    "list",
 | 
						|
    "set",
 | 
						|
    "sorted-set",
 | 
						|
    "hash",
 | 
						|
    "pubsub",
 | 
						|
    "transactions",
 | 
						|
    "connection",
 | 
						|
    "server",
 | 
						|
    "scripting",
 | 
						|
    "hyperloglog",
 | 
						|
    "cluster",
 | 
						|
    "sentinel",
 | 
						|
    "geo",
 | 
						|
    "stream",
 | 
						|
    "bitmap",
 | 
						|
    "module"
 | 
						|
};
 | 
						|
 | 
						|
const char *commandGroupStr(int index) {
 | 
						|
    return COMMAND_GROUP_STR[index];
 | 
						|
}
 | 
						|
"""
 | 
						|
    )
 | 
						|
 | 
						|
    command_list = sorted(commands.values(), key=lambda cmd: (cmd.group, cmd.name))
 | 
						|
    for command in command_list:
 | 
						|
        command.write_internal_structs(f)
 | 
						|
 | 
						|
    f.write("/* Main command table */\n")
 | 
						|
    f.write("struct COMMAND_STRUCT redisCommandTable[] = {\n")
 | 
						|
    curr_group = None
 | 
						|
    for command in command_list:
 | 
						|
        if curr_group != command.group:
 | 
						|
            curr_group = command.group
 | 
						|
            f.write("/* %s */\n" % curr_group)
 | 
						|
        f.write("{%s},\n" % command.struct_code())
 | 
						|
    f.write("{0}\n")
 | 
						|
    f.write("};\n")
 | 
						|
 | 
						|
print("All done, exiting.")
 |