Skip to content

Commit 7fc0ea8

Browse files
committed
initial commit
0 parents  commit 7fc0ea8

File tree

6 files changed

+189
-0
lines changed

6 files changed

+189
-0
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/null-char/transact
2+
3+
go 1.15

main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package main
2+
3+
func main() {
4+
5+
}

manager/manager.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package manager
2+
3+
import (
4+
"fmt"
5+
6+
s "github.com/null-char/transact/store"
7+
)
8+
9+
// Transaction consists of a next pointer which points to the next transaction in the stack
10+
// (nil if current Transaction is at the top) and a local store which resembles the data that needs to be updated
11+
// into our global store if the transaction is committed.
12+
type Transaction struct {
13+
localStore *s.Store
14+
parent *Transaction
15+
}
16+
17+
// TransactionManager manages the transaction stack
18+
type TransactionManager struct {
19+
globalStore *s.Store
20+
transactions *Transaction
21+
activeTransaction *Transaction // represents top of the stack
22+
numTransactions uint
23+
}
24+
25+
// Empty returns a bool indicating whether or not the transaction stack is empty (no ongoing transactions)
26+
func (tm TransactionManager) Empty() bool {
27+
return tm.transactions == nil
28+
}
29+
30+
// getActiveStore returns the global store if there are no active transactions and the local store (of the active transaction) if there is one
31+
func (tm TransactionManager) getActiveStore() *s.Store {
32+
if tm.Empty() {
33+
return tm.globalStore
34+
}
35+
36+
return tm.activeTransaction.localStore
37+
}
38+
39+
// PushTransaction pushes a new transaction onto the stack inheriting the local store of its parent (if it has a parent)
40+
func (tm *TransactionManager) PushTransaction() {
41+
if tm.Empty() {
42+
t := &Transaction{localStore: s.MakeNewStore(), parent: nil}
43+
tm.transactions = t
44+
tm.activeTransaction = t
45+
} else {
46+
store := s.MakeNewStore()
47+
48+
// We duplicate the data of our parent here
49+
for _, pair := range tm.activeTransaction.localStore.GetKVPairs() {
50+
store.Set(pair.First, pair.Second)
51+
}
52+
53+
t := &Transaction{localStore: store}
54+
t.parent = tm.activeTransaction
55+
tm.activeTransaction = t
56+
}
57+
58+
tm.numTransactions++
59+
}
60+
61+
// PopTransaction pops the active transaction (top of the stack) if there is one otherwise we have a stack underflow so we just log some error
62+
func (tm *TransactionManager) PopTransaction() {
63+
if tm.Empty() {
64+
fmt.Println("ERROR: There are no transactions to pop. Stack is empty.")
65+
} else {
66+
tm.activeTransaction = tm.activeTransaction.parent
67+
tm.numTransactions--
68+
}
69+
70+
}
71+
72+
func (tm *TransactionManager) Set(key string, value s.Mappable) {
73+
s := tm.getActiveStore()
74+
s.Set(key, value)
75+
}
76+
77+
func (tm *TransactionManager) Get(key string) (s.Mappable, bool) {
78+
s := tm.getActiveStore()
79+
return s.Get(key)
80+
}

store/store.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package store
2+
3+
// Store consists of a map which maps to Mappable values including methods to manipulate the map data
4+
type Store struct {
5+
data map[Key]Value
6+
}
7+
8+
// MakeNewStore constructs a new key value store
9+
func MakeNewStore() *Store {
10+
s := &Store{}
11+
return s
12+
}
13+
14+
// Set sets a new key value pair or updates it if the key already exists
15+
func (s *Store) Set(key Key, value Value) {
16+
s.data[key] = value
17+
}
18+
19+
// Get fetches the Mappable value if the key mapping exists
20+
func (s Store) Get(key Key) (Value, bool) {
21+
value, ok := s.data[key]
22+
return value, ok
23+
}
24+
25+
// Delete will remove the key value mapping associated with the provided key. Returns a status indicating a no-op if false (key mapping does not exist)
26+
func (s *Store) Delete(key Key) bool {
27+
// delete is a no-op if there is no such key so we need to manually check if key exists
28+
_, ok := s.Get(key)
29+
delete(s.data, key)
30+
return ok
31+
}
32+
33+
// Count returns an uint representing the number of times the provided value mapping exists in the store
34+
func (s Store) Count(value Value) uint {
35+
var count uint = 0
36+
37+
for _, v := range s.data {
38+
if v == value {
39+
count++
40+
}
41+
}
42+
43+
return count
44+
}
45+
46+
// GetKVPairs returns a slice of key value pairs with first field indicating the key and second field indicating the value
47+
func (s Store) GetKVPairs() []KVPair {
48+
var pairs []KVPair = make([]KVPair, 0, len(s.data))
49+
50+
for k, v := range s.data {
51+
pairs = append(pairs, KVPair{First: k, Second: v})
52+
}
53+
54+
return pairs
55+
}

store/types.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package store
2+
3+
// NOTE: We provide dummy methods just so that we can have a shared interface between these types
4+
// You can think of it as a weird way of achieving union types in Go since we don't have generics (yet)
5+
6+
// Mappable denotes all the value types that can be mapped to our store (global and local)
7+
type Mappable interface {
8+
isMappable()
9+
}
10+
11+
// Number is a Mappable with underlying type int
12+
type Number int
13+
14+
func (n Number) isMappable()
15+
16+
// String is a Mappable with underlying type string
17+
type String string
18+
19+
func (s String) isMappable()
20+
21+
type Key = string
22+
type Value = Mappable
23+
24+
type KVPair struct {
25+
First Key
26+
Second Value
27+
}

utils/printer.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
6+
s "github.com/null-char/transact/store"
7+
)
8+
9+
// PrintValue formats and prints a mappable value
10+
func PrintValue(v s.Mappable) {
11+
switch v := v.(type) {
12+
case s.Number:
13+
fmt.Printf("(integer) %d \n", v)
14+
case s.String:
15+
fmt.Printf("(string) %s \n", v)
16+
default:
17+
fmt.Printf("(unknown) %v \n", v)
18+
}
19+
}

0 commit comments

Comments
 (0)