Repository: redis/rueidis

Issue:
Generate new HGETDEL, HGETEX, HSETEX command builders
Todos: 

1. Copy the json files in [this](https://github.com/redis/redis/pull/13798/files) to https://github.com/redis/rueidis/blob/main/hack/cmds/commands.json. Please keep only the necessary fields, such as `group` and `arguments`.
2. update here: https://github.com/redis/rueidis/blob/9c59b24eb7a1a961514a4c6282629bb8e37fcbae/hack/cmds/gen.go#L684 to support the new `"type": "unix-time"` in the json.
3. run `go generate`


Hints from issue comments:
@win5923: Hi @rueian, can I take this?

Solution patch:
diff --git a/hack/cmds/commands.json b/hack/cmds/commands.json
index 65370e0..4d82103 100644
--- a/hack/cmds/commands.json
+++ b/hack/cmds/commands.json
@@ -3087,6 +3087,172 @@
     "since": "2.0.0",
     "group": "hash"
   },
+  "HGETDEL": {
+    "summary": "Returns the value of a field and deletes it from the hash",
+    "complexity": "O(N) where N is the number of specified fields",
+    "arguments": [
+      {
+        "name": "key",
+        "type": "key"
+      },
+      {
+        "name": "fields",
+        "type": "enum",
+        "enum": ["FIELDS"]
+      },
+      {
+        "name": "numfields",
+        "type": "integer"
+      },
+      {
+        "name": "field",
+        "type": "string",
+        "multiple": true
+      }
+    ],
+    "since": "8.0.0",
+    "group": "hash"
+  },
+  "HGETEX": {
+    "summary": "Get the value of one or more fields of a given hash key, and optionally set their expiration.",
+    "complexity": "O(N) where N is the number of specified fields",
+    "arguments": [
+      {
+        "name": "key",
+        "type": "key"
+      },
+      {
+        "name": "expiration",
+        "type": "oneof",
+        "optional": true,
+        "arguments": [
+          {
+            "name": "EX",
+            "type": "integer",
+            "token": "EX"
+          },
+          {
+            "name": "PX",
+            "type": "integer",
+            "token": "PX"
+          },
+          {
+            "name": "EXAT",
+            "type": "unix-time",
+            "token": "EXAT"
+          },
+          {
+            "name": "PXAT",
+            "type": "unix-time",
+            "token": "PXAT"
+          },
+          {
+            "name": "PERSIST",
+            "type": "pure-token",
+            "token": "PERSIST"
+          }
+        ]
+      },
+      {
+        "name": "fields",
+        "type": "enum",
+        "enum": ["FIELDS"]
+      },
+      {
+        "name": "numfields",
+        "type": "integer"
+      },
+      {
+        "name": "field",
+        "type": "string",
+        "multiple": true
+      }
+    ],
+    "since": "8.0.0",
+    "group": "hash"
+  },
+  "HSETEX": {
+    "summary": "Set the value of one or more fields of a given hash key, and optionally set their expiration.",
+    "complexity": "O(N) where N is the number of fields being set.",
+    "arguments": [
+      {
+        "name": "key",
+        "type": "key",
+        "key_spec_index": 0
+      },
+      {
+        "name": "condition",
+        "type": "oneof",
+        "optional": true,
+        "arguments": [
+          {
+            "name": "fnx",
+            "type": "pure-token",
+            "token": "FNX"
+          },
+          {
+            "name": "fxx",
+            "type": "pure-token",
+            "token": "FXX"
+          }
+        ]
+      },
+      {
+        "name": "expiration",
+        "type": "oneof",
+        "optional": true,
+        "arguments": [
+          {
+            "name": "EX",
+            "type": "integer",
+            "token": "EX"
+          },
+          {
+            "name": "PX",
+            "type": "integer",
+            "token": "PX"
+          },
+          {
+            "name": "EXAT",
+            "type": "unix-time",
+            "token": "EXAT"
+          },
+          {
+            "name": "PXAT",
+            "type": "unix-time",
+            "token": "PXAT"
+          },
+          {
+            "name": "KEEPTTL",
+            "type": "pure-token",
+            "token": "KEEPTTL"
+          }
+        ]
+      },
+      {
+        "name": "fields",
+        "type": "enum",
+        "enum": ["FIELDS"]
+      },
+      {
+        "name": "numfields",
+        "type": "integer"
+      },
+      {
+        "name": [
+          "field",
+          "value"
+        ],
+        "type": [
+          "string",
+          "string"
+        ],
+        "multiple": true
+      }
+    ],
+    "since": "8.0.0",
+    "group": "hash"
+  },
   "HINCRBY": {
     "summary": "Increment the integer value of a hash field by the given number",
     "complexity": "O(1)",
diff --git a/hack/cmds/gen.go b/hack/cmds/gen.go
index fb23af1..c3d9091 100644
--- a/hack/cmds/gen.go
+++ b/hack/cmds/gen.go
@@ -681,7 +681,7 @@ func toGoType(paramType string) string {
 		return "string"
 	case "double":
 		return "float64"
-	case "integer", "posix time":
+	case "integer", "posix time", "unix-time":
 		return "int64"
 	case "unsigned integer":
 		return "uint64"
diff --git a/internal/cmds/gen_hash.go b/internal/cmds/gen_hash.go
index b440300..20c199d 100644
--- a/internal/cmds/gen_hash.go
+++ b/internal/cmds/gen_hash.go
@@ -408,6 +408,168 @@ func (c HgetallKey) Cache() Cacheable {
 	return Cacheable{cs: c.cs, cf: uint16(c.cf), ks: c.ks}
 }
 
+type Hgetdel Incomplete
+
+func (b Builder) Hgetdel() (c Hgetdel) {
+	c = Hgetdel{cs: get(), ks: b.ks}
+	c.cs.s = append(c.cs.s, "HGETDEL")
+	return c
+}
+
+func (c Hgetdel) Key(key string) HgetdelKey {
+	if c.ks&NoSlot == NoSlot {
+		c.ks = NoSlot | slot(key)
+	} else {
+		c.ks = check(c.ks, slot(key))
+	}
+	c.cs.s = append(c.cs.s, key)
+	return (HgetdelKey)(c)
+}
+
+type HgetdelField Incomplete
+
+func (c HgetdelField) Field(field ...string) HgetdelField {
+	c.cs.s = append(c.cs.s, field...)
+	return c
+}
+
+func (c HgetdelField) Build() Completed {
+	c.cs.Build()
+	return Completed{cs: c.cs, cf: uint16(c.cf), ks: c.ks}
+}
+
+type HgetdelFields Incomplete
+
+func (c HgetdelFields) Numfields(numfields int64) HgetdelNumfields {
+	c.cs.s = append(c.cs.s, strconv.FormatInt(numfields, 10))
+	return (HgetdelNumfields)(c)
+}
+
+type HgetdelKey Incomplete
+
+func (c HgetdelKey) Fields() HgetdelFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetdelFields)(c)
+}
+
+type HgetdelNumfields Incomplete
+
+func (c HgetdelNumfields) Field(field ...string) HgetdelField {
+	c.cs.s = append(c.cs.s, field...)
+	return (HgetdelField)(c)
+}
+
+type Hgetex Incomplete
+
+func (b Builder) Hgetex() (c Hgetex) {
+	c = Hgetex{cs: get(), ks: b.ks}
+	c.cs.s = append(c.cs.s, "HGETEX")
+	return c
+}
+
+func (c Hgetex) Key(key string) HgetexKey {
+	if c.ks&NoSlot == NoSlot {
+		c.ks = NoSlot | slot(key)
+	} else {
+		c.ks = check(c.ks, slot(key))
+	}
+	c.cs.s = append(c.cs.s, key)
+	return (HgetexKey)(c)
+}
+
+type HgetexExpirationEx Incomplete
+
+func (c HgetexExpirationEx) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexExpirationExat Incomplete
+
+func (c HgetexExpirationExat) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexExpirationPersist Incomplete
+
+func (c HgetexExpirationPersist) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexExpirationPx Incomplete
+
+func (c HgetexExpirationPx) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexExpirationPxat Incomplete
+
+func (c HgetexExpirationPxat) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexField Incomplete
+
+func (c HgetexField) Field(field ...string) HgetexField {
+	c.cs.s = append(c.cs.s, field...)
+	return c
+}
+
+func (c HgetexField) Build() Completed {
+	c.cs.Build()
+	return Completed{cs: c.cs, cf: uint16(c.cf), ks: c.ks}
+}
+
+type HgetexFields Incomplete
+
+func (c HgetexFields) Numfields(numfields int64) HgetexNumfields {
+	c.cs.s = append(c.cs.s, strconv.FormatInt(numfields, 10))
+	return (HgetexNumfields)(c)
+}
+
+type HgetexKey Incomplete
+
+func (c HgetexKey) Ex(ex int64) HgetexExpirationEx {
+	c.cs.s = append(c.cs.s, "EX", strconv.FormatInt(ex, 10))
+	return (HgetexExpirationEx)(c)
+}
+
+func (c HgetexKey) Px(px int64) HgetexExpirationPx {
+	c.cs.s = append(c.cs.s, "PX", strconv.FormatInt(px, 10))
+	return (HgetexExpirationPx)(c)
+}
+
+func (c HgetexKey) Exat(exat int64) HgetexExpirationExat {
+	c.cs.s = append(c.cs.s, "EXAT", strconv.FormatInt(exat, 10))
+	return (HgetexExpirationExat)(c)
+}
+
+func (c HgetexKey) Pxat(pxat int64) HgetexExpirationPxat {
+	c.cs.s = append(c.cs.s, "PXAT", strconv.FormatInt(pxat, 10))
+	return (HgetexExpirationPxat)(c)
+}
+
+func (c HgetexKey) Persist() HgetexExpirationPersist {
+	c.cs.s = append(c.cs.s, "PERSIST")
+	return (HgetexExpirationPersist)(c)
+}
+
+func (c HgetexKey) Fields() HgetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HgetexFields)(c)
+}
+
+type HgetexNumfields Incomplete
+
+func (c HgetexNumfields) Field(field ...string) HgetexField {
+	c.cs.s = append(c.cs.s, field...)
+	return (HgetexField)(c)
+}
+
 type Hincrby Incomplete
 
 func (b Builder) Hincrby() (c Hincrby) {
@@ -1157,6 +1319,190 @@ func (c HsetKey) FieldValue() HsetFieldValue {
 	return (HsetFieldValue)(c)
 }
 
+type Hsetex Incomplete
+
+func (b Builder) Hsetex() (c Hsetex) {
+	c = Hsetex{cs: get(), ks: b.ks}
+	c.cs.s = append(c.cs.s, "HSETEX")
+	return c
+}
+
+func (c Hsetex) Key(key string) HsetexKey {
+	if c.ks&NoSlot == NoSlot {
+		c.ks = NoSlot | slot(key)
+	} else {
+		c.ks = check(c.ks, slot(key))
+	}
+	c.cs.s = append(c.cs.s, key)
+	return (HsetexKey)(c)
+}
+
+type HsetexConditionFnx Incomplete
+
+func (c HsetexConditionFnx) Ex(ex int64) HsetexExpirationEx {
+	c.cs.s = append(c.cs.s, "EX", strconv.FormatInt(ex, 10))
+	return (HsetexExpirationEx)(c)
+}
+
+func (c HsetexConditionFnx) Px(px int64) HsetexExpirationPx {
+	c.cs.s = append(c.cs.s, "PX", strconv.FormatInt(px, 10))
+	return (HsetexExpirationPx)(c)
+}
+
+func (c HsetexConditionFnx) Exat(exat int64) HsetexExpirationExat {
+	c.cs.s = append(c.cs.s, "EXAT", strconv.FormatInt(exat, 10))
+	return (HsetexExpirationExat)(c)
+}
+
+func (c HsetexConditionFnx) Pxat(pxat int64) HsetexExpirationPxat {
+	c.cs.s = append(c.cs.s, "PXAT", strconv.FormatInt(pxat, 10))
+	return (HsetexExpirationPxat)(c)
+}
+
+func (c HsetexConditionFnx) Keepttl() HsetexExpirationKeepttl {
+	c.cs.s = append(c.cs.s, "KEEPTTL")
+	return (HsetexExpirationKeepttl)(c)
+}
+
+func (c HsetexConditionFnx) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexConditionFxx Incomplete
+
+func (c HsetexConditionFxx) Ex(ex int64) HsetexExpirationEx {
+	c.cs.s = append(c.cs.s, "EX", strconv.FormatInt(ex, 10))
+	return (HsetexExpirationEx)(c)
+}
+
+func (c HsetexConditionFxx) Px(px int64) HsetexExpirationPx {
+	c.cs.s = append(c.cs.s, "PX", strconv.FormatInt(px, 10))
+	return (HsetexExpirationPx)(c)
+}
+
+func (c HsetexConditionFxx) Exat(exat int64) HsetexExpirationExat {
+	c.cs.s = append(c.cs.s, "EXAT", strconv.FormatInt(exat, 10))
+	return (HsetexExpirationExat)(c)
+}
+
+func (c HsetexConditionFxx) Pxat(pxat int64) HsetexExpirationPxat {
+	c.cs.s = append(c.cs.s, "PXAT", strconv.FormatInt(pxat, 10))
+	return (HsetexExpirationPxat)(c)
+}
+
+func (c HsetexConditionFxx) Keepttl() HsetexExpirationKeepttl {
+	c.cs.s = append(c.cs.s, "KEEPTTL")
+	return (HsetexExpirationKeepttl)(c)
+}
+
+func (c HsetexConditionFxx) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexExpirationEx Incomplete
+
+func (c HsetexExpirationEx) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexExpirationExat Incomplete
+
+func (c HsetexExpirationExat) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexExpirationKeepttl Incomplete
+
+func (c HsetexExpirationKeepttl) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexExpirationPx Incomplete
+
+func (c HsetexExpirationPx) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexExpirationPxat Incomplete
+
+func (c HsetexExpirationPxat) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexFieldValue Incomplete
+
+func (c HsetexFieldValue) FieldValue(field string, value string) HsetexFieldValue {
+	c.cs.s = append(c.cs.s, field, value)
+	return c
+}
+
+func (c HsetexFieldValue) Build() Completed {
+	c.cs.Build()
+	return Completed{cs: c.cs, cf: uint16(c.cf), ks: c.ks}
+}
+
+type HsetexFields Incomplete
+
+func (c HsetexFields) Numfields(numfields int64) HsetexNumfields {
+	c.cs.s = append(c.cs.s, strconv.FormatInt(numfields, 10))
+	return (HsetexNumfields)(c)
+}
+
+type HsetexKey Incomplete
+
+func (c HsetexKey) Fnx() HsetexConditionFnx {
+	c.cs.s = append(c.cs.s, "FNX")
+	return (HsetexConditionFnx)(c)
+}
+
+func (c HsetexKey) Fxx() HsetexConditionFxx {
+	c.cs.s = append(c.cs.s, "FXX")
+	return (HsetexConditionFxx)(c)
+}
+
+func (c HsetexKey) Ex(ex int64) HsetexExpirationEx {
+	c.cs.s = append(c.cs.s, "EX", strconv.FormatInt(ex, 10))
+	return (HsetexExpirationEx)(c)
+}
+
+func (c HsetexKey) Px(px int64) HsetexExpirationPx {
+	c.cs.s = append(c.cs.s, "PX", strconv.FormatInt(px, 10))
+	return (HsetexExpirationPx)(c)
+}
+
+func (c HsetexKey) Exat(exat int64) HsetexExpirationExat {
+	c.cs.s = append(c.cs.s, "EXAT", strconv.FormatInt(exat, 10))
+	return (HsetexExpirationExat)(c)
+}
+
+func (c HsetexKey) Pxat(pxat int64) HsetexExpirationPxat {
+	c.cs.s = append(c.cs.s, "PXAT", strconv.FormatInt(pxat, 10))
+	return (HsetexExpirationPxat)(c)
+}
+
+func (c HsetexKey) Keepttl() HsetexExpirationKeepttl {
+	c.cs.s = append(c.cs.s, "KEEPTTL")
+	return (HsetexExpirationKeepttl)(c)
+}
+
+func (c HsetexKey) Fields() HsetexFields {
+	c.cs.s = append(c.cs.s, "FIELDS")
+	return (HsetexFields)(c)
+}
+
+type HsetexNumfields Incomplete
+
+func (c HsetexNumfields) FieldValue() HsetexFieldValue {
+	return (HsetexFieldValue)(c)
+}
+
 type Hsetnx Incomplete
 
 func (b Builder) Hsetnx() (c Hsetnx) {



Test patch:
diff --git a/internal/cmds/gen_hash_test.go b/internal/cmds/gen_hash_test.go
index fd6bb31..7f3fedc 100644
--- a/internal/cmds/gen_hash_test.go
+++ b/internal/cmds/gen_hash_test.go
@@ -23,6 +23,13 @@ func hash0(s Builder) {
 	s.Hget().Key("1").Field("1").Cache()
 	s.Hgetall().Key("1").Build()
 	s.Hgetall().Key("1").Cache()
+	s.Hgetdel().Key("1").Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Ex(1).Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Px(1).Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Exat(1).Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Pxat(1).Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Persist().Fields().Numfields(1).Field("1").Field("1").Build()
+	s.Hgetex().Key("1").Fields().Numfields(1).Field("1").Field("1").Build()
 	s.Hincrby().Key("1").Field("1").Increment(1).Build()
 	s.Hincrbyfloat().Key("1").Field("1").Increment(1).Build()
 	s.Hkeys().Key("1").Build()
@@ -56,6 +63,24 @@ func hash0(s Builder) {
 	s.Hscan().Key("1").Cursor(1).Novalues().Build()
 	s.Hscan().Key("1").Cursor(1).Build()
 	s.Hset().Key("1").FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Ex(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Px(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Exat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Pxat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Keepttl().Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fnx().Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Ex(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Px(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Exat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Pxat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Keepttl().Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fxx().Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Ex(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Px(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Exat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Pxat(1).Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Keepttl().Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
+	s.Hsetex().Key("1").Fields().Numfields(1).FieldValue().FieldValue("1", "1").FieldValue("1", "1").Build()
 	s.Hsetnx().Key("1").Field("1").Value("1").Build()
 	s.Hstrlen().Key("1").Field("1").Build()
 	s.Hstrlen().Key("1").Field("1").Cache()