diff --git a/internal/kv/kv.go b/internal/kv/kv.go new file mode 100644 index 00000000..dedbf234 --- /dev/null +++ b/internal/kv/kv.go @@ -0,0 +1,50 @@ +package kv + +import "sync" + +type KV struct { + mu sync.RWMutex + m map[string]any +} + +func New() *KV { + return &KV{m: map[string]any{}} +} + +func (kv *KV) Set(k string, v any) { + kv.mu.Lock() + defer kv.mu.Unlock() + kv.m[k] = v +} + +func (kv *KV) Get(k string) any { //nostyle:getters + kv.mu.RLock() + defer kv.mu.RUnlock() + v, ok := kv.m[k] + if !ok { + return nil + } + return v +} + +func (kv *KV) Keys() []string { + kv.mu.RLock() + defer kv.mu.RUnlock() + keys := make([]string, 0, len(kv.m)) + for k := range kv.m { + keys = append(keys, k) + } + return keys +} + +func (kv *KV) Del(k string) { + kv.mu.RLock() + defer kv.mu.RUnlock() + delete(kv.m, k) +} + +func (kv *KV) Clear() { + kv.mu.Lock() + defer kv.mu.Unlock() + kv.m = map[string]any{} +} diff --git a/internal/kv/kv_test.go b/internal/kv/kv_test.go new file mode 100644 index 00000000..1ae5e525 --- /dev/null +++ b/internal/kv/kv_test.go @@ -0,0 +1,54 @@ +package kv + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestKV(t *testing.T) { + tests := []struct { + in any + }{ + {nil}, + {"str"}, + {3}, + {4.5}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("set/get/del %v", tt.in), func(t *testing.T) { + kv := New() + kv.Set("key", tt.in) + got := kv.Get("key") + if diff := cmp.Diff(got, tt.in); diff != "" { + t.Error(diff) + } + + { + kv.Del("key") + got := kv.Get("key") + if got != nil { + t.Errorf("got %v, want %v", got, nil) + } + } + }) + + t.Run(fmt.Sprintf("set/get/clear %v", tt.in), func(t *testing.T) { + kv := New() + kv.Set("key", tt.in) + got := kv.Get("key") + if diff := cmp.Diff(got, tt.in); diff != "" { + t.Error(diff) + } + + { + kv.Clear() + got := kv.Get("key") + if got != nil { + t.Errorf("got %v, want %v", got, nil) + } + } + }) + } +} diff --git a/kv.go b/kv.go deleted file mode 100644 index 29b66f4c..00000000 --- a/kv.go +++ /dev/null @@ -1,40 +0,0 @@ -package runn - -import "sync" - -type kv struct { - mu sync.RWMutex - m map[string]any -} - -func newKV() *kv { - return &kv{m: map[string]any{}} -} - -func (kv *kv) set(k string, v any) { - kv.mu.Lock() - defer kv.mu.Unlock() - kv.m[k] = v -} - -func (kv *kv) get(k string) any { //nostyle:getters - kv.mu.RLock() - defer kv.mu.RUnlock() - v, ok := kv.m[k] - if !ok { - return nil - } - return v -} - -func (kv *kv) del(k string) { - kv.mu.RLock() - defer kv.mu.RUnlock() - delete(kv.m, k) -} - -func (kv *kv) clear() { - kv.mu.Lock() - defer kv.mu.Unlock() - kv.m = map[string]any{} -} diff --git a/kv_test.go b/kv_test.go index 95873cee..691dae88 100644 --- a/kv_test.go +++ b/kv_test.go @@ -2,7 +2,6 @@ package runn import ( "context" - "fmt" "testing" "time" @@ -10,52 +9,6 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" ) -func TestKV(t *testing.T) { - tests := []struct { - in any - }{ - {nil}, - {"str"}, - {3}, - {4.5}, - } - for _, tt := range tests { - t.Run(fmt.Sprintf("set/get/del %v", tt.in), func(t *testing.T) { - kv := newKV() - kv.set("key", tt.in) - got := kv.get("key") - if diff := cmp.Diff(got, tt.in); diff != "" { - t.Error(diff) - } - - { - kv.del("key") - got := kv.get("key") - if got != nil { - t.Errorf("got %v, want %v", got, nil) - } - } - }) - - t.Run(fmt.Sprintf("set/get/clear %v", tt.in), func(t *testing.T) { - kv := newKV() - kv.set("key", tt.in) - got := kv.get("key") - if diff := cmp.Diff(got, tt.in); diff != "" { - t.Error(diff) - } - - { - kv.clear() - got := kv.get("key") - if got != nil { - t.Errorf("got %v, want %v", got, nil) - } - } - }) - } -} - func TestRunNWithKV(t *testing.T) { ctx := context.Background() book := "testdata/book/kv.yml" diff --git a/loop.go b/loop.go index 0e647b90..d7455182 100644 --- a/loop.go +++ b/loop.go @@ -11,8 +11,7 @@ import ( ) const ( - loopSectionKey = "loop" - storeRootKeyLoopCountIndex = "i" + loopSectionKey = "loop" ) var ( diff --git a/operator.go b/operator.go index 232c922b..aa6b46ef 100644 --- a/operator.go +++ b/operator.go @@ -23,6 +23,7 @@ import ( "github.com/k1LoW/maskedio" "github.com/k1LoW/runn/exprtrace" "github.com/k1LoW/runn/internal/deprecation" + "github.com/k1LoW/runn/internal/kv" "github.com/k1LoW/stopw" "github.com/k1LoW/waitmap" "github.com/ryo-yamaoka/otchkiss" @@ -1434,7 +1435,7 @@ type operatorN struct { opts []Option results []*runNResult runNIndex atomic.Int64 // runNIndex holds the runN execution index (starting from 0). It is incremented each time runN is executed - kv *kv + kv *kv.KV dbg *dbg mu sync.Mutex } @@ -1472,7 +1473,7 @@ func Load(pathp string, opts ...Option) (*operatorN, error) { concmax: 1, opts: opts, runNIndex: atomic.Int64{}, - kv: newKV(), + kv: kv.New(), dbg: newDBG(bk.attach), } opn.runNIndex.Store(-1) // Set index to -1 ( no runN ) @@ -1739,22 +1740,22 @@ func (opn *operatorN) CollectCoverage(ctx context.Context) (*Coverage, error) { // SetKV sets a key-value pair to runn.kv. func (opn *operatorN) SetKV(k string, v any) { - opn.kv.set(k, v) + opn.kv.Set(k, v) } // GetKV gets a value from runn.kv. func (opn *operatorN) GetKV(k string) any { //nostyle:getters - return opn.kv.get(k) + return opn.kv.Get(k) } // DelKV deletes a key-value pair from runn.kv. func (opn *operatorN) DelKV(k string) { - opn.kv.del(k) + opn.kv.Del(k) } // ClearKV clears all key-value pairs in runn.kv. func (opn *operatorN) Clear() { - opn.kv.clear() + opn.kv.Clear() } func (opn *operatorN) runN(ctx context.Context) (*runNResult, error) { diff --git a/store.go b/store.go index e1bba224..db3a27ab 100644 --- a/store.go +++ b/store.go @@ -11,24 +11,26 @@ import ( "github.com/goccy/go-json" "github.com/k1LoW/maskedio" + "github.com/k1LoW/runn/internal/kv" "github.com/mattn/go-isatty" "github.com/samber/lo" "github.com/spf13/cast" ) const ( - storeRootKeyVars = "vars" - storeRootKeySteps = "steps" - storeRootKeyParent = "parent" - storeRootKeyIncluded = "included" - storeRootKeyCurrent = "current" - storeRootKeyPrevious = "previous" - storeRootKeyEnv = "env" - storeRootKeyCookie = "cookies" - storeRootKeyNodes = "nodes" - storeRootKeyParams = "params" - storeRootKeyRunn = "runn" - storeRootKeyNeeds = "needs" + storeRootKeyVars = "vars" + storeRootKeySteps = "steps" + storeRootKeyParent = "parent" + storeRootKeyIncluded = "included" + storeRootKeyCurrent = "current" + storeRootKeyPrevious = "previous" + storeRootKeyEnv = "env" + storeRootKeyCookie = "cookies" + storeRootKeyNodes = "nodes" + storeRootKeyParams = "params" + storeRootKeyRunn = "runn" + storeRootKeyNeeds = "needs" + storeRootKeyLoopCountIndex = "i" ) const ( @@ -74,7 +76,7 @@ type store struct { useMap bool // Use map syntax in `steps:`. loopIndex *int cookies map[string]map[string]*http.Cookie - kv *kv + kv *kv.KV runNIndex int // for secret masking @@ -265,13 +267,11 @@ func (s *store) toMap() map[string]any { runnm := map[string]any{} // runn.kv if s.kv != nil { - s.kv.mu.Lock() kv := map[string]any{} - for k, v := range s.kv.m { - kv[k] = v + for _, k := range s.kv.Keys() { + kv[k] = s.kv.Get(k) } runnm[storeRunnKeyKV] = kv - s.kv.mu.Unlock() } // runn.i runnm[storeRunnKeyRunNIndex] = s.runNIndex @@ -348,13 +348,11 @@ func (s *store) toMapForDbg() map[string]any { runnm := map[string]any{} // runn.kv if s.kv != nil { - s.kv.mu.Lock() kv := map[string]any{} - for k, v := range s.kv.m { - kv[k] = v + for _, k := range s.kv.Keys() { + kv[k] = s.kv.Get(k) } runnm[storeRunnKeyKV] = kv - s.kv.mu.Unlock() } // runn.i runnm[storeRunnKeyRunNIndex] = s.runNIndex