blob: 07ed482c57ce945587375cda035087ccb3471019 [file] [log] [blame]
Joey Armstronga6af1522023-01-17 16:06:16 -05001package redis
2
3import (
4 "context"
5 "crypto/sha1"
6 "encoding/hex"
7 "io"
8 "strings"
9)
10
11type scripter interface {
12 Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd
13 EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd
14 ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd
15 ScriptLoad(ctx context.Context, script string) *StringCmd
16}
17
18var (
19 _ scripter = (*Client)(nil)
20 _ scripter = (*Ring)(nil)
21 _ scripter = (*ClusterClient)(nil)
22)
23
24type Script struct {
25 src, hash string
26}
27
28func NewScript(src string) *Script {
29 h := sha1.New()
30 _, _ = io.WriteString(h, src)
31 return &Script{
32 src: src,
33 hash: hex.EncodeToString(h.Sum(nil)),
34 }
35}
36
37func (s *Script) Hash() string {
38 return s.hash
39}
40
41func (s *Script) Load(ctx context.Context, c scripter) *StringCmd {
42 return c.ScriptLoad(ctx, s.src)
43}
44
45func (s *Script) Exists(ctx context.Context, c scripter) *BoolSliceCmd {
46 return c.ScriptExists(ctx, s.hash)
47}
48
49func (s *Script) Eval(ctx context.Context, c scripter, keys []string, args ...interface{}) *Cmd {
50 return c.Eval(ctx, s.src, keys, args...)
51}
52
53func (s *Script) EvalSha(ctx context.Context, c scripter, keys []string, args ...interface{}) *Cmd {
54 return c.EvalSha(ctx, s.hash, keys, args...)
55}
56
57// Run optimistically uses EVALSHA to run the script. If script does not exist
58// it is retried using EVAL.
59func (s *Script) Run(ctx context.Context, c scripter, keys []string, args ...interface{}) *Cmd {
60 r := s.EvalSha(ctx, c, keys, args...)
61 if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") {
62 return s.Eval(ctx, c, keys, args...)
63 }
64 return r
65}